//=============================================================================
// GF Plugins
// GF_0_CoreOfGame.js
//=============================================================================

var Imported = Imported || {};
Imported.GF_0_CoreOfGame = true;

var GF = GF || {};
GF.COG = GF.COG || {};
GF.COG.version = 1.04;
GF.COG.pluginName = document.currentScript.src.match(/([^\/]+)\.js/)[1];

//=============================================================================
/*:
 * @target MZ
 * @plugindesc [v1.04]        系统 - 游戏核心
 * @author gt50
 * @url https://afdian.net/a/ganfly
 *
 * @help
 * ============================================================================
 *  介绍
 * ============================================================================
 * 
 * 这是一个免费插件，如果您是通过付费方式获得了此插件，请立即要求收费方退款。
 * 
 * 本插件是对RMMZ默认系统机制的补充升级，添加了很多新的功能，并且包含很多
 * 后续插件用到的底层代码。
 * 
 * 主要功能包括：
 *   游戏测试选项
 *   优化选项设置
 *   货币设置
 *   菜单设置
 *   窗口设置
 *   输入设备设置
 *
 * ============================================================================
 *  前置需求
 * ============================================================================
 *
 * 这个插件只能在RPGMakerMZ上运行。
 *
 * ---- 第0层 ----
 *
 * 这个插件是第0层插件，是几乎所有GF插件的依赖，必须放在所有GF插件的上面。
 * 
 * ============================================================================
 *  备注
 * ============================================================================
 *
 * ----物品/武器/防具备注
 *    
 *     <Price: x>
 *     <价格: x>
 *      --改变该物品/武器/防具的价格，x替换为价格，会覆盖默认数据库
 *
 * ----武器/防具备注
 *
 *     <param: +x>
 *     <param: -x>
 *      --改变该武器/防具的特定基础参数，param替换为MHP, MMP, ATK, 
 *      DEF, MAT, MDF, AGI, LUK，x替换为数值，会覆盖默认数据库
 *
 * ----敌人备注
 * 
 *     <param: x>
 *      --改变该敌人的特定基础参数，param替换为MHP, MMP, ATK, 
 *      DEF, MAT, MDF, AGI, LUK, GOLD, EXP，x替换为数值，会覆盖默认数据库
 *
 * ----角色备注
 * 
 *     <Max Level: x>
 *     <等级上限: x>
 *      --改变该角色的等级上限，x替换为数值，会覆盖默认数据库
 *
 *     <Initial Level: x>
 *     <初始等级: x>
 *      --改变该角色的初始等级，x替换为数值，会覆盖默认数据库
 *
 * ----职业技能学习栏备注
 * 
 *     <Learn At Level: x>
 *     <学习等级: x>
 *      --改变该职业学习该技能的等级，x替换为数值，会覆盖默认数据库
 *
 * ============================================================================
 *  插件指令
 * ============================================================================
 *
 *   获得/失去货币
 *   强制关闭游戏
 *   开启/关闭输入设备
 *
 * ============================================================================
 *  用户规约
 * ============================================================================
 * 
 *  1. GF系列插件可用于免费或商业游戏，前提是它们是通过合法手段获得的，目
 *  前仅在qq群 568785370 发布。
 * 
 *  2. 请将此插件的作者部分找到的所有作者名单在您的游戏致谢名单中列出，目
 *  前绝大多数插件作者为gt50，少部分插件包含作者Drill_up、芯☆淡茹水。
 * 
 *  3. 在您不声称源代码属于您的前提下，您可以根据需要编辑源代码。但是，如
 *  果您对插件进行了任何更改，则您需要对修改部分负责，我们无法对用户的自定
 *  义代码负责。不允许对插件本体进行修改后二次发布，但是我们很乐意您在注明
 *  了引用的情况下将我们的插件作为基础来开发扩展插件或者在您的插件中调用或
 *  修改我们插件中的方法。
 * 
 *  4. 对于付费插件，请不要重新分发这些插件，尊重作者的劳动成果，助力RMMZ
 *  插件生态良性循环，感谢您的理解。
 *  
 *  5. 我们不对由于以下原因导致您的游戏中出现的问题负责，例如不是最新的插
 *  件版本，与其他插件的兼容问题，用户的自定义代码等。
 * 
 *  6. 如果您的游戏使用了我们的插件，我们很乐意您在游戏完成后发给我们一份
 *  展示效果。
 * 
 *  7. 其余未尽事宜，按照MIT规约处理。
 * 
 * ============================================================================
 *  更新日志
 * ============================================================================
 * 
 * [v1.00] 初版完成。
 * 
 * [v1.01] 修复Debug界面左右键调整变量值时会上下移动光标的问题
 * 
 * [v1.02] 为后续插件增加兼容性
 * 
 * [v1.03] 为后续插件增加兼容性，增加默认窗口皮肤设置
 * 
 * [v1.04] 优化默认函数，为后续插件增加兼容性，增加地图WASD移动
 *
 * ============================================================================
 *  帮助结束
 * ============================================================================
 *
 * @ ==========================================================================
 * @ 插件指令
 * @ ==========================================================================
 *
 * @command GoldChange
 * @text 获得/失去货币
 * @desc 获得/失去货币，可以超过默认货币数量限制
 *
 * @arg value
 * @text 数量
 * @desc 获得/失去货币的数量，负数代表失去
 * @default 0
 *
 * @ --------------------------------------------------------------------------
 *
 * @command ForceClose
 * @text 强制关闭游戏
 * @desc 强制关闭游戏.
 * 
 * @ --------------------------------------------------------------------------
 *
 * @command InputMenuSwitch
 * @text 开启/关闭输入设备
 * @desc 开启/关闭输入设备
 *
 * @arg Type
 * @text 类型
 * @type select
 * @option 战斗界面-键盘
 * @value BattleKeyboard
 * @option 战斗界面-手柄
 * @value BattlePad
 * @option 战斗界面-鼠标
 * @value BattleCusor
 * @option 战斗界面-触屏
 * @value BattleTouch
 * @option 地图界面-键盘
 * @value MapKeyboard
 * @option 地图界面-手柄
 * @value MapPad
 * @option 地图界面-鼠标
 * @value MapCusor
 * @option 地图界面-触屏
 * @value MapTouch
 * @option 地图界面-键盘/手柄方向键移动
 * @value MapKeyboardMove
 * @option 地图界面-键盘WASD键移动
 * @value MapWASDMove   
 * @option 地图界面-鼠标左键移动
 * @value MapLeftMove
 * @option 地图界面-鼠标右键菜单
 * @value MapRightMenu
 * @option 地图界面-触屏双指菜单
 * @value MapTouchMenu
 * @option 菜单界面-键盘
 * @value MenuKeyboard
 * @option 菜单界面-手柄
 * @value MenuPad
 * @option 菜单界面-鼠标
 * @value MenuCusor
 * @option 菜单界面-触屏
 * @value MenuTouch
 * @desc 开启/关闭输入设备
 * @default BattleKeyboard
 * 
 * @arg State
 * @text 开启/关闭
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开启/关闭输入设备
 * @default true
 *
 * @ ==========================================================================
 * @ 插件参数
 * @ ==========================================================================
 *
 * @param TestSet
 * @text 游戏测试选项
 * @type struct<TestSet>
 * @desc 游戏测试选项
 * @default {"PlayerTest":"","AutoStart":"true","OpenConsole":"false","OnSwitches":"[]","CommonEvent":"0","StartCode":"\"\"","BattleTest":"","BTestItems":"true","BTestWeapons":"true","BTestArmors":"true","BTestAddedQuantity":"90"}
 *
 * @param QoLSet
 * @text 优化选项设置
 * @type struct<QoLSet>
 * @desc 优化选项设置
 * @default {"AniMirrorOffset":"false","CommonEvent":"0","NoTileShadows":"false","SmoothImageRender":"true","RequireFocus":"false","SmartEventCollision":"true","UpdateCusorStyle":"true"}
 *
 * @param GoldSet
 * @text 货币设置
 * @type struct<GoldSet>
 * @desc 货币设置
 * @default {"GoldMax":"99999999","GoldIcon":"313","GoldOverlap":"数不清","GoldWindowType":"图标+单位+数字"}
 * 
 * @param MenuSet
 * @text 菜单设置
 * @type struct<MenuSet>
 * @desc 菜单设置
 * @default {"ScenePreCreate":"true","BackgroundMode":"地图截图+模糊滤镜","CustomBackFile":"","BackOpacity":"255"}
 *
 * @param WindowSet
 * @text 窗口设置
 * @type struct<WindowSet>
 * @desc 窗口设置
 * @default {"WindowSkin":"Window","MutlipleLayers":"true","UseRoundRect":"true","LineHeight":"36","ItemPadding":"8","TranslucentOpacity":"160","OpenSpeed":"32","CloseSpeed":"32","ColSpacing":"8","RowSpacing":"4","ItemHeight":"8"}
 *
 * @param InputSet
 * @text 输入设备设置
 * @type struct<InputSet>
 * @desc 输入设备设置
 * @default {"BattleKeyboard":"true","BattlePad":"true","BattleCusor":"true","BattleTouch":"true","MapKeyboard":"true","MapPad":"true","MapCusor":"true","MapTouch":"true","MapKeyboardMove":"true","MapWASDMove":"true","MapLeftMove":"true","MapRightMenu":"true","MapTouchMenu":"true","MenuKeyboard":"true","MenuPad":"true","MenuCusor":"true","MenuTouch":"true"}
 *
 */
/* ----------------------------------------------------------------------------
 * struct<TestSet>
 * ----------------------------------------------------------------------------
 */
/*~struct~TestSet:
 *
 * @param PlayerTest
 * @text ====游戏测试====
 *
 * @param AutoStart
 * @text 是否跳过标题
 * @parent PlayerTest
 * @type boolean
 * @on 跳过
 * @off 不跳过
 * @desc 是否在测试模式下自动跳过标题
 * @default true
 *
 * @param OpenConsole
 * @text 是否初始打开debug窗口
 * @parent PlayerTest
 * @type boolean
 * @on 打开
 * @off 不打开
 * @desc 是否初始打开debug窗口
 * @default false
 *
 * @param OnSwitches
 * @text 打开的开关
 * @parent PlayerTest
 * @type switch[]
 * @desc 测试时默认打开的开关
 * @default []
 *
 * @param CommonEvent
 * @text 游戏开始公共事件
 * @parent PlayerTest
 * @type common_event
 * @desc 测试时的游戏开始公共事件.
 * @default 0
 *
 * @param StartCode
 * @text 游戏开始代码
 * @parent PlayerTest
 * @type note
 * @desc 测试游戏开始时运行的代码
 * @default ""
 *
 * @param BattleTest
 * @text ====战斗测试====
 *
 * @param BTestItems
 * @text 背包携带物品
 * @parent BattleTest
 * @type boolean
 * @on 携带
 * @off 不携带
 * @desc 是否在战斗测试时在背包中携带所有物品?
 * @default true
 *
 * @param BTestWeapons
 * @text 背包携带武器
 * @parent BattleTest
 * @type boolean
 * @on 携带
 * @off 不携带
 * @desc 是否在战斗测试时在背包中携带所有武器?
 * @default true
 *
 * @param BTestArmors
 * @text 背包携带防具
 * @parent BattleTest
 * @type boolean
 * @on 携带
 * @off 不携带
 * @desc 是否在战斗测试时在背包中携带所有防具?
 * @default true
 *
 * @param BTestAddedQuantity
 * @text 携带的数量
 * @parent BattleTest
 * @type number
 * @min 1
 * @desc 设置战斗测试时每种物品携带的数量。
 * @default 90
 *
 */
/* ----------------------------------------------------------------------------
 * struct<QoLSet>
 * ----------------------------------------------------------------------------
 */
/*~struct~QoLSet:
 *
 * @param AniMirrorOffset
 * @text 是否镜像动画偏移
 * @type boolean
 * @on 镜像
 * @off 不镜像
 * @desc 当动画被镜像时，是否同时镜像X轴偏移量？
 * @default false
 *
 * @param CommonEvent
 * @text 游戏开始公共事件
 * @type common_event
 * @desc 游戏开始公共事件.
 * @default 0
 *
 * @param NoTileShadows
 * @text 是否隐藏图块阴影
 * @type boolean
 * @on 隐藏
 * @off 显示
 * @desc 是否隐藏图块阴影.
 * @default false
 *
 * @param SmoothImageRender
 * @text 是否对绘制图像平滑处理
 * @type boolean
 * @on 平滑
 * @off 像素化
 * @desc 选择是否对绘制图像进行平滑，像素化有利于对图像进行放缩
 * @default true
 *
 * @param RequireFocus
 * @text 是否只在被选中时才刷新游戏窗口
 * @type boolean
 * @on 选中时刷新
 * @off 始终保持刷新
 * @desc 是否只在被选中时才刷新游戏窗口，如果是，游戏窗口在后台会暂停
 * @default false
 *
 * @param SmartEventCollision
 * @text 是否开启智能事件碰撞
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 使事件只有在“与角色相同”优先级时才能相互碰撞。
 * @default true
 *
 * @param UpdateCusorStyle
 * @text 是否开启鼠标样式变化
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开启鼠标样式变化后，鼠标在能点击的位置会变成手型
 * @default true
 *
 */
/* ---------------------------------------------------------------------------
 * struct<GoldSet>
 * ---------------------------------------------------------------------------
 */
/*~struct~GoldSet:
 *
 * @param GoldMax
 * @text 货币上限
 * @type number
 * @min 1
 * @desc 队伍持有货币上限
 * Default 99999999
 * @default 99999999
 *
 * @param GoldIcon
 * @text 货币图标
 * @desc 货币图标
 * @default 314
 *
 * @param GoldOverlap
 * @text 大量货币名称
 * @desc 大量货币名称
 * @default 数不清
 *
 * @param GoldWindowType
 * @text 货币窗口显示格式
 * @type select
 * @option 数字+图标
 * @option 图标+单位+数字
 * @desc 货币窗口显示格式
 * @default 图标+单位+数字
 */
/* ----------------------------------------------------------------------------
 * struct<MenuSet>
 * ----------------------------------------------------------------------------
 */
/*~struct~MenuSet:
 *
 * @param ScenePreCreate
 * @text 是否启用菜单预加载
 * @type boolean
 * @on 启用
 * @off 禁用
 * @desc 启用菜单预加载后,进入下一个菜单前会预加载其内容，可以一定程度上缓解切换菜单时的卡顿。
 * @default true
 *
 * @param BackgroundMode
 * @text 菜单默认背景类型
 * @type select
 * @option 地图截图           (直接截图)
 * @value 地图截图
 * @option 地图截图+模糊滤镜  (比较消耗性能,mz默认效果)
 * @value 地图截图+模糊滤镜
 * @option 地图截图后模糊处理 (模糊效果一般,mv默认效果)
 * @value 地图截图后模糊处理
 * @option 自定义图片         (最省事)
 * @value 自定义图片
 * @desc 菜单默认背景类型。
 * @default 地图截图后模糊处理
 * 
 * @param CustomBackFile
 * @text 资源-自定义图片
 * @type file
 * @dir img/
 * @require 1
 * @desc 菜单默认背景类型为自定义图片时，图片文件
 * @default 
 *
 * @param BackOpacity
 * @text 菜单默认背景透明度
 * @type number
 * @max 255
 * @desc 菜单默认背景透明度，默认192
 * @default 192
 *
 */
/* ----------------------------------------------------------------------------
 * struct<WindowSet>
 * ----------------------------------------------------------------------------
 */
/*~struct~WindowSet:
 *
 * @param WindowSkin
 * @text 默认窗口皮肤
 * @type file
 * @dir img/system/
 * @desc 默认窗口皮肤
 * @default Window
 *
 * @param MutlipleLayers
 * @text 是否启用层状窗口
 * @parent WindowDefaults
 * @type boolean
 * @on 是
 * @off 否
 * @desc 是否启用层状窗口
 * @default true
 *
 * @param UseRoundRect
 * @text 是否绘制圆角矩形
 * @parent WindowDefaults
 * @type boolean
 * @on 是
 * @off 否
 * @desc 是否将默认的直角矩形更改为绘制圆角矩形
 * @default true
 *
 * @param LineHeight
 * @text 行高
 * @parent WindowDefaults
 * @desc 行高，默认 36
 * @default 36
 *
 * @param ItemPadding
 * @text 四周间隙
 * @parent WindowDefaults
 * @desc 四周间隙，默认 8
 * @default 8
 *
 * @param TranslucentOpacity
 * @text 半透明时的不透明度
 * @parent WindowDefaults
 * @type number
 * @min 1
 * @decimals 0
 * @desc 半透明时的不透明度，默认 160
 * @default 160
 *
 * @param OpenSpeed
 * @text 窗口开启速度
 * @parent WindowDefaults
 * @type number
 * @min 1
 * @decimals 0
 * @desc 窗口开启速度，默认 32
 * @default 32
 *
 * @param CloseSpeed
 * @text 窗口关闭速度
 * @parent WindowDefaults
 * @type number
 * @min 1
 * @decimals 0
 * @desc 窗口关闭速度，默认 32
 * @default 32
 *
 * @param ColSpacing
 * @text 每列间隙
 * @parent WindowDefaults
 * @desc 选择窗口每列间隙.，默认 8
 * @default 8
 *
 * @param RowSpacing
 * @text 每行间隙
 * @parent WindowDefaults
 * @desc 选择窗口每行间隙，默认 4
 * @default 4
 *
 * @param ItemHeight
 * @text 选项间隙
 * @parent SelectableItems
 * @desc 选项窗口的选项间隙，默认 8
 * @default 8
 *
 */
/* ----------------------------------------------------------------------------
 * struct<InputSet>
 * ----------------------------------------------------------------------------
 */
/*~struct~InputSet:
 *
 * @param BattleKeyboard
 * @text 战斗界面-键盘
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-战斗界面-键盘。
 * @default true
 *
 * @param BattlePad
 * @text 战斗界面-手柄
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-战斗界面-手柄。
 * @default true
 *
 * @param BattleCusor
 * @text 战斗界面-鼠标
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-战斗界面-鼠标。
 * @default true
 *
 * @param BattleTouch
 * @text 战斗界面-触屏
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-战斗界面-触屏。
 * @default true
 *
 * @param MapKeyboard
 * @text 地图界面-键盘
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-地图界面-键盘。
 * @default true
 *
 * @param MapPad
 * @text 地图界面-手柄
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-地图界面-手柄。
 * @default true
 *
 * @param MapCusor
 * @text 地图界面-鼠标
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-地图界面-鼠标。
 * @default true
 *
 * @param MapTouch
 * @text 地图界面-触屏
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-地图界面-触屏
 * @default true
 *
 * @param MapKeyboardMove
 * @text 地图界面-键盘/手柄方向键移动
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 若设置关闭，则地图界面中键盘或手柄方向键移动的功能会被禁用。
 * @default true
 *
 * @param MapWASDMove
 * @text 地图界面-键盘WASD键移动
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 若设置关闭，则地图界面中键盘WASD键移动的功能会被禁用。
 * @default true
 *
 * @param MapLeftMove
 * @text 地图界面-鼠标左键移动
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 若设置关闭，则地图界面中鼠标左键移动的功能会被禁用。
 * @default true
 *
 * @param MapRightMenu
 * @text 地图界面-鼠标右键菜单
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 若设置关闭，则地图界面中鼠标右键直接进入菜单的功能会被禁用。
 * @default true
 *
 * @param MapTouchMenu
 * @text 地图界面-触屏双指菜单
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 若设置关闭，则地图界面中触屏按下两个手指后进入菜单的功能会被禁用。
 * @default true
 *
 * @param MenuKeyboard
 * @text 菜单界面-键盘
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-菜单界面-键盘。
 * @default true
 *
 * @param MenuPad
 * @text 菜单界面-手柄
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-菜单界面-手柄。
 * @default true
 *
 * @param MenuCusor
 * @text 菜单界面-鼠标
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-菜单界面-鼠标。
 * @default true
 *
 * @param MenuTouch
 * @text 菜单界面-触屏
 * @parent MenuSwitch
 * @type boolean
 * @on 开启
 * @off 关闭
 * @desc 开关-菜单界面-触屏。
 * @default true
 * 
 */
//=============================================================================

//=============================================================================
// Parameter Variables
//=============================================================================

GF.Parameters = PluginManager.parameters(GF.COG.pluginName);
GF.Param = GF.Param || {};

GF.Param.COGTestSet = (() => {
	const set = JSON.parse(GF.Parameters['TestSet']);
	set.AutoStart = eval(set.AutoStart ||'true');
	set.OpenConsole = eval(set.OpenConsole || 'false');
	set.OnSwitches = JSON.parse(set.OnSwitches || []).map(id => Number(id));
	set.CommonEvent = Number(set.CommonEvent || 0);
	set.StartCode = new Function(JSON.parse(set.StartCode || ""));
	set.BTestItems = eval(set.BTestItems || 'true');
	set.BTestWeapons = eval(set.BTestWeapons || 'true');
	set.BTestArmors = eval(set.BTestArmors || 'true');
	set.BTestAddedQuantity = Number(set.BTestAddedQuantity || 90);
	return set;
})();
GF.Param.COGQoLSet = (() => {
	const set = JSON.parse(GF.Parameters['QoLSet']);
    set.ScenePreCreate = eval(set.ScenePreCreate || 'true');
	set.AniMirrorOffset = eval(set.AniMirrorOffset || 'false');
	set.CommonEvent = Number(set.CommonEvent || 0);
	set.NoTileShadows = eval(set.NoTileShadows || 'false');
	set.SmoothImageRender = eval(set.SmoothImageRender || 'true');
	set.RequireFocus = eval(set.RequireFocus || 'false');
	set.SmartEventCollision = eval(set.SmartEventCollision || 'true');
	set.UpdateCusorStyle = eval(set.UpdateCusorStyle || 'true');
	return set;
})();
GF.Param.COGGoldSet = (() => {
	const set = JSON.parse(GF.Parameters['GoldSet']);
	set.GoldMax = Number(set.GoldMax || 99999999);
	set.GoldIcon = Number(set.GoldIcon || 314);
	return set;
})();
GF.Param.COGMenuSet = (() => {
	const set = JSON.parse(GF.Parameters['MenuSet']);
	set.ScenePreCreate = eval(set.ScenePreCreate || 'true');
    set.BackOpacity = Number(set.BackOpacity || 192);
	return set;
})();
GF.Param.COGWindowSet = (() => {
	const set = JSON.parse(GF.Parameters['WindowSet']);
    set.WindowSkin = String(set.WindowSkin || 'Window');
	set.MutlipleLayers = eval(set.MutlipleLayers || 'true');
	set.UseRoundRect = eval(set.UseRoundRect || 'true');
	set.LineHeight = Number(set.LineHeight || 36);
	set.ItemPadding = Number(set.ItemPadding || 8);
	set.TranslucentOpacity = Number(set.TranslucentOpacity || 160);
	set.OpenSpeed = Number(set.OpenSpeed || 32);
	set.CloseSpeed = Number(set.CloseSpeed || 32);
	set.ColSpacing = Number(set.ColSpacing || 8);
	set.RowSpacing = Number(set.RowSpacing || 4);
	set.ItemHeight = Number(set.ItemHeight || 8);
	return set;
})();
GF.Param.COGInputSet = (() => {
	const set = JSON.parse(GF.Parameters['InputSet']);
    set.BattleKeyboard = eval(set.BattleKeyboard || 'true');
    set.BattlePad = eval(set.BattlePad || 'true');
    set.BattleCusor = eval(set.BattleCusor || 'true');
    set.BattleTouch = eval(set.BattleTouch || 'true');

    set.MapKeyboard = eval(set.MapKeyboard || 'true');
    set.MapPad = eval(set.MapPad || 'true');
    set.MapCusor = eval(set.MapCusor || 'true');
    set.MapTouch = eval(set.MapTouch || 'true');

    set.MapKeyboardMove = eval(set.MapKeyboardMove || 'true');
    set.MapWASDMove = eval(set.MapWASDMove || 'true');
    
    set.MapLeftMove = eval(set.MapLeftMove || 'true');
    set.MapRightMenu = eval(set.MapRightMenu || 'true');
	set.MapTouchMenu = eval(set.MapTouchMenu || 'true');

    set.MenuKeyboard = eval(set.MenuKeyboard || 'true');
    set.MenuPad = eval(set.MenuPad || 'true');
    set.MenuCusor = eval(set.MenuCusor || 'true');
    set.MenuTouch = eval(set.MenuTouch || 'true');
	return set;
})();

//=============================================================================
// CanvasRenderingContext2D
//=============================================================================

/**
 * 设置扇形路径。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} r - 圆半径。 
 * @param {number} angle1 - 起始角度。（正上方 0 度，顺时针）
 * @param {number} angle2 - 结束角度。
 */
CanvasRenderingContext2D.prototype.setSector = function(x, y, r, angle1, angle2) {
    angle1 = (angle1 + 270) % 360;
    angle2 = (angle2 + 270) % 360;
    this.save();
    this.beginPath();
    this.moveTo(x, y);
    this.arc(x, y, r, angle1 * Math.PI / 180, angle2 * Math.PI / 180, false);
    this.closePath();    
    this.restore();
    return this;
};

/**
 * 设置圆角矩形路径。
 * @param {number} width - 宽度。
 * @param {number} height - 高度。
 * @param {number} radius - 圆角半径。 
 */
CanvasRenderingContext2D.prototype.setRoundRectPath = function(width, height, radius) {
    this.beginPath(); 
    this.arc(width - radius, height - radius, radius, 0, Math.PI / 2); 
    this.lineTo(radius, height);
    this.arc(radius, height - radius, radius, Math.PI / 2, Math.PI); 
    this.lineTo(0, radius);  
    this.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2);  
    this.lineTo(width - radius, 0); 
    this.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2);  
    this.lineTo(width, height - radius);
    this.closePath();
	this.restore();
    return this;
};

/**
 * 根据一系列坐标点，设置折线路径。
 * @param {Array} points - 点坐标数组。
 */
CanvasRenderingContext2D.prototype.setBrokenLinePath = function(points) {
    this.save();
    this.beginPath();
    this.moveTo(points[0].x, points[0].y);
    for (let i = 1; i < points.length; i++) {
        this.lineTo(points[i].x, points[i].y);
    }
    this.lineTo(points[0].x, points[0].y);
    this.closePath();
	return this;
};

//=============================================================================
// Bitmap
//=============================================================================

/**
 * 将模糊效果应用于bitmap.
 */
Bitmap.prototype.blur = function() {
    for (let i = 0; i < 2; i++) {
        const w = this.width;
        const h = this.height;
        const canvas = this._canvas;
        const context = this.context;
        const tempCanvas = document.createElement('canvas');
        const tempContext = tempCanvas.getContext('2d');
        tempCanvas.width = w + 2;
        tempCanvas.height = h + 2;
        tempContext.drawImage(canvas, 0, 0, w, h, 1, 1, w, h);
        tempContext.drawImage(canvas, 0, 0, w, 1, 1, 0, w, 1);
        tempContext.drawImage(canvas, 0, 0, 1, h, 0, 1, 1, h);
        tempContext.drawImage(canvas, 0, h - 1, w, 1, 1, h + 1, w, 1);
        tempContext.drawImage(canvas, w - 1, 0, 1, h, w + 1, 1, 1, h);
        context.save();
        context.fillStyle = 'black';
        context.fillRect(0, 0, w, h);
        context.globalCompositeOperation = 'lighter';
        context.globalAlpha = 1 / 9;
        for (let y = 0; y < 3; y++) {
            for (let x = 0; x < 3; x++) {
                context.drawImage(tempCanvas, x, y, w, h, 0, 0, w, h);
            }
        }
        context.restore();
    }
    this._baseTexture.update();
};

Bitmap.copy = function(bitmap) {
    const width = bitmap.width;
    const height = bitmap.height;
    const newBitmap = new Bitmap(width, height);
    newBitmap.normalBlt(bitmap, 0, 0, width, height, 0, 0);
    return newBitmap;
};

Bitmap.prototype._drawTextBody = function(text, tx, ty, maxWidth) {
	this._setFillStyle(text, tx, ty, maxWidth);
    this._startFillText(text, tx, ty, maxWidth);
};

Bitmap.prototype._setFillStyle = function(text, tx, ty, maxWidth) {
	const context = this.context;
	context.fillStyle = this.textColor;
};

Bitmap.prototype._startFillText = function(text, tx, ty, maxWidth) {
	const context = this.context;
	context.fillText(text, tx, ty, maxWidth);
};

GF.COG.Bitmap_blt = Bitmap.prototype.blt;
Bitmap.prototype.blt = function(source, sx, sy, sw, sh, dx, dy, dw, dh) {
    if (!GF.Param.COGQoLSet.SmoothImageRender) {
        sx = Math.floor(sx);
        sy = Math.floor(sy);
        sw = Math.floor(sw);
        sh = Math.floor(sh);
        dx = Math.floor(dx);
        dy = Math.floor(dy);
        dw = Math.floor(dw);
        dh = Math.floor(dh);
        this.context.imageSmoothingEnabled = false;
    }
    GF.COG.Bitmap_blt.call(this, source, sx, sy, sw, sh, dx, dy, dw, dh);
	this.context.imageSmoothingEnabled = true;
};
/**
 * Performs a block transfer.
 *
 * @param {Bitmap} source - The bitmap to draw.
 * @param {number} sx - The x coordinate in the source.
 * @param {number} sy - The y coordinate in the source.
 * @param {number} sw - The width of the source image.
 * @param {number} sh - The height of the source image.
 * @param {number} dx - The x coordinate in the destination.
 * @param {number} dy - The y coordinate in the destination.
 * @param {number} [dw=sw] The width to draw the image in the destination.
 * @param {number} [dh=sh] The height to draw the image in the destination.
 */
Bitmap.prototype.normalBlt = function(source, sx, sy, sw, sh, dx, dy, dw, dh) {
    dw = dw || sw;
    dh = dh || sh;
    try {
        const image = source._canvas || source._image;
        this.context.globalCompositeOperation = "source-over";
        this.context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
        this._baseTexture.update();
    } catch (e) {
        //
    }
};
/**
 * 绘制直线。
 * @param {number} x1 - 起点 X 坐标。
 * @param {number} y1 - 起点 Y 坐标。
 * @param {number} x2 - 终点 X 坐标。
 * @param {number} y2 - 终点 Y 坐标。
 * @param {string} color - 颜色。
 * @param {number} lineWidth - 宽度。
 */
Bitmap.prototype.drawLine = function(x1, y1, x2, y2, color = 'black', lineWidth = 1) {
	const context = this.context;
	context.strokeStyle = color;
	context.lineWidth = lineWidth;
	context.beginPath();
	context.moveTo(x1, y1);
	context.lineTo(x2, y2);
	context.closePath();
	context.stroke();
	context.restore();
	this._baseTexture.update();
};

/**
 * 填充扇形。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} r - 圆半径。
 * @param {number} angle1 - 起始角度。（正上方 0 度，顺时针）
 * @param {number} angle2 - 结束角度。
 * @param {string} color - 颜色。
 */
Bitmap.prototype.drawSector = function(x, y, r, angle1, angle2, color = 'black') {
    const context = this.context;
    context.fillStyle = color;
    context.setSector(x, y, r, angle1, angle2);
	context.fill();
    context.restore();
    this._baseTexture.update();
};

/**
 * 描绘扇形框。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} r - 圆半径。
 * @param {number} angle1 - 起始角度。（正上方 0 度，顺时针）
 * @param {number} angle2 - 结束角度。
 * @param {string} color - 颜色。
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.strokeSector = function(
    x, y, r, angle1, angle2, color = 'black', lineWidth = 1
) {
    const context = this.context;
	context.lineWidth = lineWidth;
    context.strokeStyle = color;
    context.setSector(x, y, r, angle1, angle2);
	context.stroke();
    context.restore();
    this._baseTexture.update();
};

/**
 * 渐变填充扇形。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} r - 圆半径。
 * @param {number} scale - 开始渐变的比例（0 ~ 1）。
 * @param {number} angle1 - 起始角度。（正上方 0 度，顺时针）
 * @param {number} angle2 - 结束角度。
 * @param {string} color1 - 颜色1。
 * @param {string} color2 - 颜色2。
 */
Bitmap.prototype.gradientDrawSector = function(
    x, y, r, scale, angle1, angle2, color1, color2
) {
    const context = this.context;
    const rg = context.createRadialGradient(x, y, r, x, y, r * scale);
    rg.addColorStop(0, color1);
	rg.addColorStop(1, color2);
    context.fillStyle = rg;
    context.setSector(x, y, r, angle1, angle2);
	context.fill();
    context.restore();
    this._baseTexture.update();
};

/**
 * 描绘圆形框。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} radius - 圆半径。
 * @param {string} color - 颜色。
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.strokeCircle = function(x, y, radius, color = 'black', lineWidth = 1) {
    const context = this.context;
    context.save();
	context.lineWidth = lineWidth;
    context.strokeStyle = color;
    context.beginPath();
    context.arc(x, y, radius, 0, Math.PI * 2, false);
    context.stroke();
    context.restore();
    this._baseTexture.update();
};

/**
 * 渐变填充圆形。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} r - 圆半径。
 * @param {number} scale - 开始渐变的比例（0 ~ 1）。
 * @param {string} color1 - 颜色1。
 * @param {string} color2 - 颜色2。
 */
Bitmap.prototype.gradientDrawCircle = function(x, y, r, scale, color1, color2) {
    const context = this.context;
    const rg = context.createRadialGradient(x, y, r, x, y, r * scale);
    context.beginPath();
	context.arc(x, y , r, 0, Math.PI * 2, true);
	context.closePath();
	rg.addColorStop(0, color1);
	rg.addColorStop(1, color2);
	context.fillStyle = rg;
	context.fill();
    context.restore();
    this._baseTexture.update();
};

/**
 * 清除圆形区域。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} r - 圆半径。
 */
Bitmap.prototype.clearCircle = function (x, y, r) {
    const context = this.context;
    context.save();
    context.beginPath();
    context.arc(x, y, r, 0, 2 * Math.PI, false);
    context.clip();
    context.clearRect(x - r, y - r, 2 * r, 2 * r);
    this._baseTexture.update();
};

/**
 * 填充椭圆。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} width - 长轴长度。
 * @param {number} height - 短轴长度。
 * @param {string} color - 颜色。
 */
Bitmap.prototype.drawEllipse = function (x, y, width, height, color) {
    if (width <= 0 || height <= 0) return;
    const halfWidth = width / 2;
    const halfHeight = height / 2;
    const context = this.context;
    context.save();
    context.fillStyle = color;
    context.beginPath();
    context.ellipse(halfWidth + x, halfHeight + y, halfWidth, halfHeight, 0, 0, Math.PI * 2);
    context.fill();
    context.restore();
    this._baseTexture.update();
};

/**
 * 绘制椭圆框。
 * @param {number} x - 圆心 X 坐标。
 * @param {number} y - 圆心 Y 坐标。
 * @param {number} width - 长轴长度。
 * @param {number} height - 短轴长度。
 * @param {string} color - 描边颜色。
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.strokeEllipse = function (
    x, y, width, height, color = 'black', lineWidth = 1
) {
    if (width <= 0 || height <= 0) return;
    const halfWidth = width / 2;
    const halfHeight = height / 2;
    const context = this.context;
	context.lineWidth = lineWidth;
    context.save();
    context.strokeStyle = color;
    context.beginPath();
    context.ellipse(halfWidth + x, halfHeight + y, halfWidth, halfHeight, 0, 0, Math.PI * 2);
    context.stroke();
    context.restore();
    this._baseTexture.update();
};

/**
 * 填充圆角矩形。
 * @param {number} x - 矩形 X 坐标
 * @param {number} y - 矩形 Y 坐标
 * @param {number} width - 矩形 宽度
 * @param {number} height - 矩形 高度
 * @param {string} color - 填充颜色
 * @param {number} radius - 圆角半径
 */
Bitmap.prototype.fillRoundRect = function(x, y, width, height, color = 'black', radius = 5) { 
    if (width < 2 * radius || height < 2 * radius) {
        return this.fillNormalRect(x, y, width, height, color);
    }
    const context = this.context;
    context.save();
    context.translate(x, y); 
    context.setRoundRectPath(width, height, radius);
    context.fillStyle = color;
    context.fill();
    context.restore();
    this._baseTexture.update();
};

/**
 * 绘制圆角矩形框。
 * @param {number} x - 矩形 X 坐标
 * @param {number} y - 矩形 Y 坐标
 * @param {number} width - 矩形 宽度
 * @param {number} height - 矩形 高度
 * @param {string} color - 描边颜色
 * @param {number} radius - 圆角半径
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.strokeRoundRect = function(
    x, y, width, height, color = 'black', radius = 5, lineWidth = 1
) {         
    if (width < 2 * radius || height < 2 * radius) {
        return this.strokeNormalRect(x, y, width, height, color);
    }
    const context = this.context;
    context.save();
    context.translate(x, y); 
    context.setRoundRectPath(width, height, radius);
    context.lineWidth = lineWidth;
    context.strokeStyle = color;
    context.stroke();
    context.restore();
    this._baseTexture.update();
};

/**
 * 渐变填充圆角矩形。
 * @param {number} x - 矩形 X 坐标
 * @param {number} y - 矩形 Y 坐标
 * @param {number} width - 矩形 宽度
 * @param {number} height - 矩形 高度
 * @param {string} color1 - 颜色1。
 * @param {string} color2 - 颜色2。
 * @param {boolean} vertical - true代表竖向渐变
 * @param {number} radius - 圆角半径
 */
Bitmap.prototype.gradientFillRoundRect = function(
    x, y, width, height, color1, color2, vertical, radius = 5
) {
	if (width < 2 * radius || height < 2 * radius) {
        return this.gradientFillNormalRect(x, y, width, height, color1, color2, vertical);
    }
    const context = this.context;
    const x1 = vertical ? x : x + width;
    const y1 = vertical ? y + height : y;
    const grad = context.createLinearGradient(x, y, x1, y1);
    grad.addColorStop(0, color1);
    grad.addColorStop(1, color2);
    context.save();
	context.translate(x, y); 
    context.setRoundRectPath(width, height, radius);
    context.fillStyle = grad;
    context.fill();
    context.restore();
    this._baseTexture.update();
};

/**
 * 渐变绘制圆角矩形框。
 * @param {number} x - 矩形 X 坐标
 * @param {number} y - 矩形 Y 坐标
 * @param {number} width - 矩形 宽度
 * @param {number} height - 矩形 高度
 * @param {string} color1 - 颜色1。
 * @param {string} color2 - 颜色2。
 * @param {boolean} vertical - true代表竖向渐变
 * @param {number} radius - 圆角半径
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.gradientStrokeRoundRect = function(
    x, y, width, height, color1, color2, vertical, radius = 5, lineWidth = 1
) {
	if (width < 2 * radius || height < 2 * radius) {
        return this.gradientStrokeNormalRect(x, y, width, height, color1, color2, vertical, lineWidth);
    }
    const context = this.context;
    const x1 = vertical ? x : x + width;
    const y1 = vertical ? y + height : y;
    const grad = context.createLinearGradient(x, y, x1, y1);
    grad.addColorStop(0, color1);
    grad.addColorStop(1, color2);
    context.save();
	context.translate(x, y); 
    context.setRoundRectPath(width, height, radius);
	context.lineWidth = lineWidth;
    context.strokeStyle = grad;
    context.stroke();
    context.restore();
    this._baseTexture.update();
};

/**
 * 填充矩形。(原始函数)
 * @param {number} x - 矩形 X 坐标
 * @param {number} y - 矩形 Y 坐标
 * @param {number} width - 矩形 宽度
 * @param {number} height - 矩形 高度
 * @param {string} color - 填充颜色
 */
Bitmap.prototype.fillNormalRect = function(x, y, width, height, color) {
    const context = this.context;
    context.save();
    context.fillStyle = color;
    context.fillRect(x, y, width, height);
    context.restore();
    this._baseTexture.update();
};

/**
 * 绘制矩形框。(原始函数)
 * @param {number} x - 矩形 X 坐标
 * @param {number} y - 矩形 Y 坐标
 * @param {number} width - 矩形 宽度
 * @param {number} height - 矩形 高度
 * @param {string} color - 描边颜色
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.strokeNormalRect = function(
x, y, width, height, color, lineWidth = 1
) {
    const context = this.context;
    context.save();
    context.lineWidth = lineWidth;
    context.strokeStyle = color;
    context.strokeRect(x, y, width, height);
    context.restore();
    this._baseTexture.update();
};

/**
 * 渐变填充矩形。(原始函数)
 * @param {number} x - 矩形 X 坐标
 * @param {number} y - 矩形 Y 坐标
 * @param {number} width - 矩形 宽度
 * @param {number} height - 矩形 高度
 * @param {string} color1 - 颜色1。
 * @param {string} color2 - 颜色2。
 * @param {boolean} vertical - true代表竖向渐变
 */
Bitmap.prototype.gradientFillNormalRect = function(
    x, y, width, height, color1, color2, vertical
) {
    const context = this.context;
    const x1 = vertical ? x : x + width;
    const y1 = vertical ? y + height : y;
    const grad = context.createLinearGradient(x, y, x1, y1);
    grad.addColorStop(0, color1);
    grad.addColorStop(1, color2);
    context.save();
    context.fillStyle = grad;
    context.fillRect(x, y, width, height);
    context.restore();
    this._baseTexture.update();
};

/**
 * 渐变绘制矩形框。
 * @param {number} x - 矩形 X 坐标
 * @param {number} y - 矩形 Y 坐标
 * @param {number} width - 矩形 宽度
 * @param {number} height - 矩形 高度
 * @param {string} color1 - 颜色1。
 * @param {string} color2 - 颜色2。
 * @param {boolean} vertical - true代表竖向渐变
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.gradientStrokeNormalRect = function(
    x, y, width, height, color1, color2, vertical, lineWidth = 1
) {
    const context = this.context;
    const x1 = vertical ? x : x + width;
    const y1 = vertical ? y + height : y;
    const grad = context.createLinearGradient(x, y, x1, y1);
    grad.addColorStop(0, color1);
    grad.addColorStop(1, color2);
    context.save();
    context.lineWidth = lineWidth;
    context.strokeStyle = grad;
    context.strokeRect(x, y, width, height);
    context.restore();
    this._baseTexture.update();
};

/**
 * 完全填充。(原始函数)
 * @param {string} color - 填充颜色
 */
Bitmap.prototype.fillNormalAll = function(color) {
    this.fillNormalRect(0, 0, this.width, this.height, color);
};

if (GF.Param.COGWindowSet.UseRoundRect) {
	
Bitmap.prototype.fillRect = function(x, y, width, height, color) {
    this.fillRoundRect(x, y, width, height, color);
};

Bitmap.prototype.strokeRect = function(x, y, width, height, color, radius, lineWidth) {
    this.strokeRoundRect(x, y, width, height, color, radius, lineWidth);
};

Bitmap.prototype.gradientFillRect = function(
    x, y, width, height, color1, color2, vertical
) {
    this.gradientFillRoundRect(x, y, width, height, color1, color2, vertical);
};

}

/**
 * 填充多边形
 * @param {array} points - 顶点Point列表
 * @param {string} color - 填充颜色
 */
Bitmap.prototype.fillPolygon = function(points, color = 'black') {
	if (!points || points.length < 3) return;
    const context = this.context;
    context.setBrokenLinePath(points);
    context.fillStyle = color;
    context.fill();
    context.restore();
    this._baseTexture.update();
};

/**
 * 绘制多边形框
 * @param {array} points - 顶点Point列表
 * @param {string} color - 描边颜色
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.strokePolygon = function(points, color = 'black', lineWidth = 1) {
	if (!points || !points.length) return;
    const context = this.context;
    context.setBrokenLinePath(points);
    context.strokeStyle = color;
    context.lineWidth = lineWidth;
    context.stroke();
    context.restore();
    this._baseTexture.update();
};

/**
 * 渐变填充多边形
 * @param {number} x - 圆心X坐标
 * @param {number} y - 圆心Y坐标
 * @param {array} points - 顶点Point列表
 * @param {string} color1 - 填充颜色1
 * @param {string} color2 - 填充颜色2
 */
Bitmap.prototype.gradientFillPolygon = function(x, y, radius, points, color1, color2) {
	if (!points || points.length < 3) return;
    const context = this.context;
    context.setBrokenLinePath(points);
	const grad = context.createRadialGradient(x, y, 1, x, y, radius);
    grad.addColorStop(0, color1);
    grad.addColorStop(1, color2);
    context.fillStyle = grad;
    context.fill();
    context.restore();
    this._baseTexture.update();
};
 
/**
 * 绘制放射图
 * @param {number} x - 圆心X坐标
 * @param {number} y - 圆心Y坐标
 * @param {array} points - 顶点Point列表
 * @param {string} color - 描边颜色
 * @param {number} lineWidth - 描边宽度。
 */
Bitmap.prototype.strokeRadial = function(x, y, points, color = 'black', lineWidth = 1) {
	if (!points || points.length < 3) return;
    const context = this.context;
    context.save();
    context.beginPath();
    for (let i = 0; i < points.length; i++) {
		context.moveTo(x, y);
        context.lineTo(points[i].x, points[i].y);
    }
    context.strokeStyle = color;
    context.lineWidth = lineWidth;
    context.stroke();
    context.restore();
    this._baseTexture.update();
};

/**
 * 描绘散点
 * @param {array} points - 点坐标数组。
 * @param {number} radius - 半径
 * @param {string} color - 面图的填充颜色，默认白色。
 */
Bitmap.prototype.drawVertex = function (points, radius = 2, color = 'black') {
    for (let i = 0; i < points.length; i++) {
        const p = points[i];
		this.drawCircle(p.x, p.y, radius, color);
        this.strokeCircle(p.x, p.y, radius, 'black');
    }
};

/**
 * 描绘雷达图。 
 * @param {number} x - 圆心X坐标
 * @param {number} y - 圆心Y坐标
 * @param {number} radius - 半径
 * @param {array} arr - 所有线条参数对象的数组，参数对象包含当前值cur、最大值max、名称name
 * @param {string} lineColor - 底图线条颜色，默认黑色。
 * @param {number} lineWidth - 底图线条像素宽度，默认 1。
 * @param {string} fillColor1 - 面图的填充颜色1，默认白色。
 * @param {string} fillColor2 - 面图的填充颜色2。
 * @param {object} verData - 每个线条顶点的小圆点数据对象，包含半径radius和颜色color, 
 * 不代入此参数则不描绘小圆点。
 */
Bitmap.prototype.drawRadarChart = function (
    x, y, radius, arr, lineColor = 'black', lineWidth = 1, fillColor1 = 'white', fillColor2, verData
) {
    if (!arr || arr.length < 3) return;
    if ((this.width / 2) < radius || (this.height / 2) < radius) return;
    if (x < radius || y < radius || (x + radius) > this.width || (y + radius) > this.height) return;
	fillColor2 = fillColor2 || fillColor1;
    const vertexNum = arr.length;
    this.drawRadarLine(x, y, radius, vertexNum, lineColor, lineWidth);
    const points = [];
    for (let i = 0; i < vertexNum; i++) {
        const obj = arr[i];
        if (obj.min === undefined) obj.min = 0;
        if (obj.max === undefined) obj.max = 100;
        if (obj.cur === undefined) obj.cur = 0;
        const angle = i * (360 / vertexNum);
        points.push(this.getVertexByAngle(x, y, radius, angle, (obj.cur - obj.min) / (obj.max - obj.min)));
		const point_110 = this.getVertexByAngle(x, y, radius, angle, 1.1);
		const name = obj.name;
		if (!name) continue;
		this.drawText(name, point_110.x - 50, point_110.y - 18, 100, 36, 'center');
    }
	this.strokePolygon(points, lineColor, lineWidth);
	this.gradientFillPolygon(x, y, radius, points, fillColor1, fillColor2);
	if (verData) {
		this.drawVertex(points, verData.radius, verData.color);
	}
};

/**
 * 描绘雷达图背景线
 * @param {number} x - 中心 X 坐标
 * @param {number} y - 中心 Y 坐标
 * @param {number} radius - 描绘半径
 * @param {number} vertexNum - 节点数
 * @param {string} lineColor - 底图线条颜色，默认黑色。
 * @param {number} lineWidth - 底图线条像素宽度，默认 1。
 */
Bitmap.prototype.drawRadarLine = function (
    x, y, radius, vertexNum, lineColor = 'black', lineWidth = 1
) {
    const points_100 = [];
    const points_80 = [];
	const points_60 = [];
	const points_40 = [];
	const points_20 = [];
    for (let i = 0; i < vertexNum; i++) {
        const angle = i * (360 / vertexNum);
        points_100.push(this.getVertexByAngle(x, y, radius, angle, 1));
        points_80.push(this.getVertexByAngle(x, y, radius, angle, 0.8));
		points_60.push(this.getVertexByAngle(x, y, radius, angle, 0.6));
		points_40.push(this.getVertexByAngle(x, y, radius, angle, 0.4));
		points_20.push(this.getVertexByAngle(x, y, radius, angle, 0.2));
    }
	this.strokePolygon(points_100, lineColor, lineWidth);
	this.strokePolygon(points_80, lineColor, lineWidth);
	this.strokePolygon(points_60, lineColor, lineWidth);
	this.strokePolygon(points_40, lineColor, lineWidth);
	this.strokePolygon(points_20, lineColor, lineWidth);
	this.strokeRadial(x, y, points_100, lineColor, lineWidth);
	return points_100;
};

/**
 * 根据角度和半径比例，获取坐标点
 * @param {number} ox - 中心 X 坐标
 * @param {number} oy - 中心 Y 坐标
 * @param {number} radius - 半径。
 * @param {number} angle - 角度
 * @param {number} rate - 顶点占半径的比例
 */
Bitmap.prototype.getVertexByAngle = function (ox, oy, radius, angle, rate) {
    const length = rate * radius;
    const x = ox + length * Math.sin(angle * Math.PI / 180);
    const y = oy - length * Math.cos(angle * Math.PI / 180);
    return new Point(x, y);
};

//=============================================================================
// Graphics
//=============================================================================

document.oncontextmenu = () => false;

Graphics.printFullError = function (name, message) {
	if (!this._errorPrinter) {
        this._createErrorPrinter();
    }
    this._errorPrinter.innerHTML = this._makeFullErrorHtml(name, message);
	this._wasLoading = this.endLoading();
    this._applyCanvasFilter();
};

Graphics._makeFullErrorHtml = function (name, message) {
	const stack = this.processErrorStackMessage();
	const nameDiv = document.createElement("div");
    const messageDiv = document.createElement("div");
    nameDiv.id = "errorName";
    messageDiv.id = "errorMessage";
    nameDiv.innerHTML = Utils.escapeHtml(stack[0] || "");
    messageDiv.innerHTML = Utils.escapeHtml((name + ': ' + message) || "");
    return nameDiv.outerHTML + messageDiv.outerHTML + stack[1] + stack[2];
};

Graphics.processErrorStackMessage = function () {
    return [
        '游戏出现了一个bug, 请按F8后截图向作者报告.',
        '<br><font color="yellow"><b>按F8查看详细.</b></font><br>',
        '<br><font color="yellow"><b>按F5重启游戏.</b></font><br>'
    ];
};

Graphics.showRetryButton = function(retry) {
    const button = document.createElement("button");
    button.id = "retryButton";
    button.innerHTML = "重试";
    button.ontouchstart = e => e.stopPropagation();
    button.onclick = () => {
        Graphics.eraseError();
        retry();
    };
    this._errorPrinter.appendChild(button);
    button.focus();
};

Graphics._showFPS = false;
Graphics._fullScreen = false;
Object.defineProperty(Graphics, 'showFPS', {
    get: function() {
        return this._showFPS;
    },
    set: function(value) {
        this._showFPS = value;
        if (this._showFPS) {
			this._fpsCounter._boxDiv.style.display = "block";
			this._fpsCounter._showFps = true;
		} else {
			this._fpsCounter._boxDiv.style.display = "none";
		}
    },
    configurable: true
});

Object.defineProperty(Graphics, 'fullScreen', {
    get: function() {
        return this._fullScreen;
    },
    set: function(value) {
        this._fullScreen = value;
        if (this._fullScreen) {
			if (!this._isFullScreen()) {
				this._requestFullScreen();
			}
		} else {
			if (this._isFullScreen()) {
				this._cancelFullScreen();
			}
		}
    },
    configurable: true
});

Object.defineProperty(Graphics, 'stretchScreen', {
    get: function() {
        return this._stretchEnabled;
    },
    set: function(value) {
        this._stretchEnabled = !!value;
		this._updateAllElements();
    },
    configurable: true
});

//=============================================================================
// TouchInput
//=============================================================================

/**
 * 设置鼠标样式。
 * @param {string} cursor - 鼠标样式['default', 'pointer']
 */
TouchInput.setCursor = function (cursor = 'default') {
    if (this._cursor === cursor) return;
    this._cursor = cursor;
    document.body.style.cursor = this._cursor;
};

/**
 * 返回鼠标样式。
 * @return {string} 鼠标样式['default', 'pointer']
 */
TouchInput.cursorStyle = function () {
    return this._cursor;
};

/**
 * 返回鼠标是否可用。
 * @return {boolean} 鼠标是否可用
 */
TouchInput.isMouseEnabled = function () {
    if (!SceneManager._scene) return true;
    if (!$gameSystem) return true;
    if (SceneManager.isCurrentScene(Scene_Map)) {
        if (!$gameSystem._inputEnable_MapCusor) {
            return false;
        }
    } else if (SceneManager.isCurrentScene(Scene_Battle)) {
        if (!$gameSystem._inputEnable_BattleCusor) {
            return false;
        }
    } else {
        if (!$gameSystem._inputEnable_MenuCusor) {
            return false;
        }
    }
    return true;
};

/**
 * 返回触屏是否可用。
 * @return {boolean} 触屏是否可用
 */
TouchInput.isTouchPadEnabled = function () {
    if (!SceneManager._scene) return true;
    if (!$gameSystem) return true;
    if (SceneManager.isCurrentScene(Scene_Map)) {
        if (!$gameSystem._inputEnable_MapTouch) {
            return false;
        }
    } else if (SceneManager.isCurrentScene(Scene_Battle)) {
        if (!$gameSystem._inputEnable_BattleTouch) {
            return false;
        }
    } else {
        if (!$gameSystem._inputEnable_MenuTouch) {
            return false;
        }
    }
    return true;
};

/**
 * 滚轮向上[一帧]
 * @returns {boolean} 滚轮是否向上滚动
 */
TouchInput.isWheelUp = function() {
	const threshold = 20;
	return TouchInput.wheelY <= -threshold;
};

/**
 * 滚轮向下[一帧]
 * @returns {boolean} 滚轮是否向下滚动
 */
TouchInput.isWheelDown = function() {
	const threshold = 20;
	return TouchInput.wheelY >= threshold;
};

/**
 * 滚轮按下[持续]
 * @returns 
 */
TouchInput.isMiddlePressed = function() {		
	return this._middlePressed;
};

/**
 * 滚轮按下[一帧]
 * @returns 
 */
TouchInput.isMiddleTriggered = function(){		
	return this._currentState.middleTriggered;
};

/**
 * 滚轮释放[一帧]
 * @returns 
 */
TouchInput.isMiddleReleased = function(){		
	return this._currentState.middleReleased;
};

/**
 * 右键按下[持续]
 * @returns 
 */
TouchInput.isRightPressed = function(){		
	return this._rightPressed;
};

/**
 * 右键按下[一帧]
 * @returns {boolean} 右键是否按下
 */
TouchInput.isRightTriggered = function(){	
	return this._currentState.rightTriggered;
};

/**
 * 右键释放[一帧]
 * @returns 
 */
TouchInput.isRightReleased = function(){	
	return this._currentState.rightReleased;
};

GF.COG.TouchInput_clear = TouchInput.clear;
TouchInput.clear = function () {
    GF.COG.TouchInput_clear.call(this);
    this._middlePressed = false;
    this._rightPressed = false;
    this._middlePressedTime = 0;
    this._rightPressedTime = 0;
    this._cursor = 'default';
    this.setCursor();
};

GF.COG.TouchInput_update = TouchInput.update;
TouchInput.update = function () {
    GF.COG.TouchInput_update.call(this);
    if (this.isMiddlePressed()) {
        this._middlePressedTime++;
    } 
    if (this.isRightPressed()) {
        this._rightPressedTime++;
    } 
};

GF.COG.TouchInput__createNewState = TouchInput._createNewState;
TouchInput._createNewState = function() {
    const data = GF.COG.TouchInput__createNewState.call(this);
    data.middleTriggered = false;
    data.middleReleased = false;
    data.rightTriggered = false;
    data.rightReleased = false;
    return data;
};

GF.COG.TouchInput__onMouseDown = TouchInput._onMouseDown;
TouchInput._onMouseDown = function (event) {
    if (!this.isMouseEnabled()) return;
    GF.COG.TouchInput__onMouseDown.call(this, event);
};

TouchInput._onMiddleButtonDown = function(event) {
	const x = Graphics.pageToCanvasX(event.pageX);
	const y = Graphics.pageToCanvasY(event.pageY);
	if (Graphics.isInsideCanvas(x, y)) {
		this._middlePressed = true;
		this._middlePressedTime = 0;
        this._onMiddleTrigger(x, y);
	}
};

TouchInput._onRightButtonDown = function(event) {
    if ($gameSystem && 
        !$gameSystem._inputEnable_MapRightMenu && 
        SceneManager.isCurrentScene(Scene_Map)) {
        return;
    }
    const x = Graphics.pageToCanvasX(event.pageX);
    const y = Graphics.pageToCanvasY(event.pageY);
    if (Graphics.isInsideCanvas(x, y)) {
		this._rightPressed = true;
		this._rightPressedTime = 0;
        this._onRightTrigger(x, y);
        this._onCancel(x, y);
    }
};

GF.COG.TouchInput__onMouseMove = TouchInput._onMouseMove;
TouchInput._onMouseMove = function (event) {
    if (!this.isMouseEnabled()) return;
    GF.COG.TouchInput__onMouseMove.call(this, event);
};

TouchInput._onMouseUp = function(event) {
    if (!this.isMouseEnabled()) return;
	if (event.button === 0) {
		this._onLeftButtonUp(event);
	} else if (event.button === 1) {
		this._onMiddleButtonUp(event);
	} else if (event.button === 2) {
		this._onRightButtonUp(event);
	}
};

TouchInput._onLeftButtonUp = function(event) {
    const x = Graphics.pageToCanvasX(event.pageX);
    const y = Graphics.pageToCanvasY(event.pageY);
    this._mousePressed = false;
    this._onRelease(x, y);
};

TouchInput._onMiddleButtonUp = function(event) {	
    const x = Graphics.pageToCanvasX(event.pageX);
    const y = Graphics.pageToCanvasY(event.pageY);	
	this._middlePressed = false;
    this._onMiddleRelease(x, y);
};

TouchInput._onRightButtonUp = function(event) {
    const x = Graphics.pageToCanvasX(event.pageX);
    const y = Graphics.pageToCanvasY(event.pageY);
	this._rightPressed = false;
    this._onRightRelease(x, y);
};

GF.COG.TouchInput__onWheel = TouchInput._onWheel;
TouchInput._onWheel = function (event) {
    if (!this.isMouseEnabled()) return;
    GF.COG.TouchInput__onWheel.call(this, event);
};

GF.COG.TouchInput__onTouchStart = TouchInput._onTouchStart;
TouchInput._onTouchStart = function (event) {
    if (!this.isTouchPadEnabled()) return;
    if ($gameSystem && 
        !$gameSystem._inputEnable_MapTouchMenu && 
        SceneManager.isCurrentScene(Scene_Map) && 
        event.touches.length >= 2) {
        this._inputEnable_forbid_menu = true;
    }
    GF.COG.TouchInput__onTouchStart.call(this, event);
};

GF.COG.TouchInput__onTouchMove = TouchInput._onTouchMove;
TouchInput._onTouchMove = function (event) {
    if (!this.isTouchPadEnabled()) return;
    GF.COG.TouchInput__onTouchMove.call(this, event);
};

GF.COG.TouchInput__onTouchEnd = TouchInput._onTouchEnd;
TouchInput._onTouchEnd = function (event) {
    if (!this.isTouchPadEnabled()) return;
    GF.COG.TouchInput__onTouchEnd.call(this, event);
};

GF.COG.TouchInput__onTouchCancel = TouchInput._onTouchCancel;
TouchInput._onTouchCancel = function (event) {
    if (!this.isTouchPadEnabled()) return;
    GF.COG.TouchInput__onTouchCancel.call(this, event);
};

GF.COG.TouchInput__onCancel = TouchInput._onCancel;
TouchInput._onCancel = function (x, y) {
    if (this._inputEnable_forbid_menu) {
        this._inputEnable_forbid_menu = false;
        return;
    }
    GF.COG.TouchInput__onCancel.call(this, x, y);
};

TouchInput._onMiddleTrigger = function (x, y) {
    this._newState.middleTriggered = true;
    this._x = x;
    this._y = y;
};

TouchInput._onRightTrigger = function (x, y) {
    this._newState.rightTriggered = true;
    this._x = x;
    this._y = y;
};

TouchInput._onMiddleRelease = function(x, y) {
    this._newState.middleReleased = true;
    this._x = x;
    this._y = y;
};

TouchInput._onRightRelease = function(x, y) {
    this._newState.rightReleased = true;
    this._x = x;
    this._y = y;
};

//=============================================================================
// Input
//=============================================================================

/**
 * 返回键盘是否可用。
 * @return {boolean} 键盘是否可用
 */
Input.isKeyboardEnabled = function () {
    if (!SceneManager._scene) return true;
    if (!$gameSystem) return true;
    if (SceneManager.isCurrentScene(Scene_Map)) {
        if (!$gameSystem._inputEnable_MapKeyboard) {
            return false;
        }
    } else if (SceneManager.isCurrentScene(Scene_Battle)) {
        if (!$gameSystem._inputEnable_BattleKeyboard) {
            return false;
        }
    } else {
        if (!$gameSystem._inputEnable_MenuKeyboard) {
            return false;
        }
    }
    return true;
};

/**
 * 返回手柄是否可用。
 * @return {boolean} 手柄是否可用
 */
Input.isPadEnabled = function () {
    if (!SceneManager._scene) return true;
    if (!$gameSystem) return true;
    if (SceneManager.isCurrentScene(Scene_Map)) {
        if (!$gameSystem._inputEnable_MapPad) {
            return false;
        }
    } else if (SceneManager.isCurrentScene(Scene_Battle)) {
        if (!$gameSystem._inputEnable_BattlePad) {
            return false;
        }
    } else {
        if (!$gameSystem._inputEnable_MenuPad) {
            return false;
        }
    }
    return true;
};

/**
 * 键盘按住[保持]
 * @param {string} keyName - 键名
 * @returns {boolean} 该键是否按住
 */
GF.COG.Input_isPressed = Input.isPressed;
Input.isPressed = function(keyName) {
    if (keyName === 'pageup') {
        return GF.COG.Input_isPressed.call(this, 'pageup') || GF.COG.Input_isPressed.call(this, 'q');
    }
    if (keyName === 'pagedown') {
        return GF.COG.Input_isPressed.call(this, 'pagedown') || GF.COG.Input_isPressed.call(this, 'w');
    }
    if (keyName === 'ok') {
        return GF.COG.Input_isPressed.call(this, 'enter') || GF.COG.Input_isPressed.call(this, 'space') || GF.COG.Input_isPressed.call(this, 'z');
    }
    if (keyName === 'control') {
        return GF.COG.Input_isPressed.call(this, 'control') || GF.COG.Input_isPressed.call(this, 'alt');
    }
    if (keyName === 'escape') {
        return GF.COG.Input_isPressed.call(this, 'escape') || GF.COG.Input_isPressed.call(this, 'insert') || GF.COG.Input_isPressed.call(this, 'x');
    }
    return GF.COG.Input_isPressed.call(this, keyName);
};

/**
 * 键盘按下[一帧]
 * @param {string} keyName - 键名
 * @returns {boolean} 该键是否按下
 */
GF.COG.Input_isTriggered = Input.isTriggered;
Input.isTriggered = function(keyName) {
    if (keyName === 'pageup') {
        return GF.COG.Input_isTriggered.call(this, 'pageup') || GF.COG.Input_isTriggered.call(this, 'q');
    }
    if (keyName === 'pagedown') {
        return GF.COG.Input_isTriggered.call(this, 'pagedown') || GF.COG.Input_isTriggered.call(this, 'w');
    }
    if (keyName === 'ok') {
        return GF.COG.Input_isTriggered.call(this, 'enter') || GF.COG.Input_isTriggered.call(this, 'space') || GF.COG.Input_isTriggered.call(this, 'z');
    }
    if (keyName === 'control') {
        return GF.COG.Input_isTriggered.call(this, 'control') || GF.COG.Input_isTriggered.call(this, 'alt');
    }
    if (keyName === 'escape') {
        return GF.COG.Input_isTriggered.call(this, 'escape') || GF.COG.Input_isTriggered.call(this, 'insert') || GF.COG.Input_isTriggered.call(this, 'x');
    }
    return GF.COG.Input_isTriggered.call(this, keyName);
};

/**
 * Checks whether a key is just pressed or a key repeat occurred.
 *
 * @param {string} keyName - The mapped name of the key.
 * @returns {boolean} True if the key is repeated.
 */
GF.COG.Input_isRepeated = Input.isRepeated;
Input.isRepeated = function(keyName) {
    if (keyName === 'pageup') {
        return GF.COG.Input_isRepeated.call(this, 'pageup') || GF.COG.Input_isRepeated.call(this, 'q');
    }
    if (keyName === 'pagedown') {
        return GF.COG.Input_isRepeated.call(this, 'pagedown') || GF.COG.Input_isRepeated.call(this, 'w');
    }
    if (keyName === 'ok') {
        return GF.COG.Input_isRepeated.call(this, 'enter') || GF.COG.Input_isRepeated.call(this, 'space') || GF.COG.Input_isRepeated.call(this, 'z');
    }
    if (keyName === 'control') {
        return GF.COG.Input_isRepeated.call(this, 'control') || GF.COG.Input_isRepeated.call(this, 'alt');
    }
    if (keyName === 'escape') {
        return GF.COG.Input_isRepeated.call(this, 'escape') || GF.COG.Input_isRepeated.call(this, 'insert') || GF.COG.Input_isRepeated.call(this, 'x');
    }
    return GF.COG.Input_isRepeated.call(this, keyName);
};

/**
 * Checks whether a key is kept depressed.
 *
 * @param {string} keyName - The mapped name of the key.
 * @returns {boolean} True if the key is long-pressed.
 */
GF.COG.Input_isLongPressed = Input.isLongPressed;
Input.isLongPressed = function(keyName) {
    if (keyName === 'pageup') {
        return GF.COG.Input_isLongPressed.call(this, 'pageup') || GF.COG.Input_isLongPressed.call(this, 'q');
    }
    if (keyName === 'pagedown') {
        return GF.COG.Input_isLongPressed.call(this, 'pagedown') || GF.COG.Input_isLongPressed.call(this, 'w');
    }
    if (keyName === 'ok') {
        return GF.COG.Input_isLongPressed.call(this, 'enter') || GF.COG.Input_isLongPressed.call(this, 'space') || GF.COG.Input_isLongPressed.call(this, 'z');
    }
    if (keyName === 'control') {
        return GF.COG.Input_isLongPressed.call(this, 'control') || GF.COG.Input_isLongPressed.call(this, 'alt');
    }
    if (keyName === 'escape') {
        return GF.COG.Input_isLongPressed.call(this, 'escape') || GF.COG.Input_isLongPressed.call(this, 'insert') || GF.COG.Input_isLongPressed.call(this, 'x');
    }
    return GF.COG.Input_isLongPressed.call(this, keyName);
};

/**
 * 键盘释放[一帧]
 * @param {string} keyName - 键名
 * @returns {boolean} 该键是否释放
 */
Input.isReleased = function (keyName) {
    if (this._isEscapeCompatible(keyName) && this.isReleased("escape")) {
        return true;
    } else {
        return this._latestButtonCopy === keyName && !this._currentState[keyName];
    }
};

/**
 * 任意键按下[一帧]
 * @returns {boolean} 任意键是否按下
 */
Input.isAnyKeyTriggered = function () {
    const keys = Object.keys(this.keyMapper);
    for (let i = 0; i < keys.length; i++) {
        const keyName = this.keyMapper[keys[i]];
        if (this._latestButton === keyName && this._pressedTime === 0) {
            return true;
        }
    }
    return false;
};

/**
 * 任意键释放[一帧]
 * @returns {boolean} 任意键是否释放
 */
Input.isAnyKeyReleased = function () { 
    const keys = Object.keys(this.keyMapper);
    for (let i = 0; i < keys.length; i++) {
        const keyName = this.keyMapper[keys[i]];
        if (this._latestButtonCopy === keyName && !this._currentState[keyName]) {
            return true;
        }
    }
    return false;
};

/**
 * 额外的全键盘值
 * @type Object
 */
Input.extendKeyMapper = {
    192: '`',
    49: '1',
    50: '2',
    51: '3',
    52: '4',
    53: '5',
    54: '6',
    55: '7',
    56: '8',
    57: '9',
    48: '0',
    189: '-',
    187: '=',
    8:'backspace',

    81: "q", // pageup
    87: "w", // pagedown
    69: 'e',
    82: 'r',
    84: 't',
    89: 'y',
    85: 'u',
    73: 'i',
    79: 'o',
    80: 'p',
    219: '[',
    221: ']',
    220: '\\',

    20: 'capslock',
    65: 'a',
    83: 's',
    68: 'd',
    70: 'f',
    71: 'g',
    72: 'h',
    74: 'j',
    75: 'k',
    76: 'l',
    186: ';',
    222: "'",
    13: "enter", // ok

    90: "z", // ok
    88: "x", // escape
    67: 'c',
    86: 'v',
    66: 'b',
    78: 'n',
    77: 'm',
    188: ',',
    190: '.',
    191: '/',

    18: "alt", // control
    32: "space", // ok
    
    45: "insert", // escape
    36: 'home',
	46: 'delete',
    35: 'end'
};

(() => {
    const keys = Object.keys(Input.extendKeyMapper);
    for (let i = 0; i < keys.length; i++) {
        const key = keys[i];
        Input.keyMapper[key] = Input.extendKeyMapper[key];
    }
})();

GF.COG.Input_update = Input.update;
Input.update = function () {
    this._latestButtonCopy = this._latestButton;
    GF.COG.Input_update.call(this);
};

GF.COG.Input__onKeyDown = Input._onKeyDown;
Input._onKeyDown = function (event) {
    if (!this.isKeyboardEnabled()) return;
    GF.COG.Input__onKeyDown.call(this, event);
};

GF.COG.Input__onKeyUp = Input._onKeyUp;
Input._onKeyUp = function (event) {
    if (!this.isKeyboardEnabled()) return;
    GF.COG.Input__onKeyUp.call(this, event);
};

GF.COG.Input__updateGamepadState = Input._updateGamepadState;
Input._updateGamepadState = function (gamepad) {
    if (!this.isPadEnabled()) return;
    GF.COG.Input__updateGamepadState.call(this, gamepad);
};

GF.COG.Input__signX = Input._signX;
Input._signX = function() {
    if ($gameSystem && $gameSystem._inputEnable_MapWASDMove) {
        const left = (this.isPressed("left") || this.isPressed("a")) ? 1 : 0;
        const right = (this.isPressed("right") || this.isPressed("d")) ? 1 : 0;
        return right - left;
    }
    return GF.COG.Input__signX.call(this);
};

GF.COG.Input__signY = Input._signY;
Input._signY = function() {
    if ($gameSystem && $gameSystem._inputEnable_MapWASDMove) {
        const up = (this.isPressed("up") || this.isPressed("w")) ? 1 : 0;
        const down = (this.isPressed("down") || this.isPressed("s")) ? 1 : 0;
        return down - up;
    }
    return GF.COG.Input__signY.call(this);
};

//=============================================================================
// Tilemap
//=============================================================================

GF.COG.Tilemap_addShadow = Tilemap.prototype._addShadow;
Tilemap.prototype._addShadow = function(layer, shadowBits, dx, dy) {
	if (GF.Param.COGQoLSet.NoTileShadows) return;
	GF.COG.Tilemap_addShadow.call(this, layer, shadowBits, dx, dy);
};

//=============================================================================
// DataManager
//=============================================================================

DataManager.isClass = function(item) {
    return item && item.learnings !== undefined;
};

DataManager.isSkill = function(item) {
    return item && item.stypeId !== undefined;
};

DataManager.isItem = function(item) {
    return item && item.itypeId !== undefined;
};

DataManager.isWeapon = function(item) {
    return item && item.wtypeId !== undefined;
};

DataManager.isArmor = function(item) {
    return item && item.atypeId !== undefined;
};

DataManager.isEquip = function(item) {
    return item && item.etypeId !== undefined;
};

DataManager.isState = function(item) {
    return item && item.autoRemovalTiming !== undefined;
};

GF.COG.DataManager_setupNewGame = DataManager.setupNewGame;
DataManager.setupNewGame = function () {
    GF.COG.DataManager_setupNewGame.call(this);
    this.setupNewGameTestPlayAssist();
};

DataManager.setupNewGameTestPlayAssist = function () {
	if (!Utils.isNwjs()) return;
	if (Utils.isOptionValid('test')) {
		const switches = GF.Param.COGTestSet.OnSwitches;
		for (let i = 0; i < switches.length; i++) {
			const switchId = switches[i];
			if (switchId > 0) {
				$gameSwitches.setValue(switchId, true);
			}
		}
		const commonEvent = GF.Param.COGTestSet.CommonEvent;
		if (commonEvent > 0) {
			$gameTemp.reserveCommonEvent(commonEvent);
		}
		const func = GF.Param.COGTestSet.StartCode;
		func.call(this);
	} else {
		const commonEvent = GF.Param.COGQoLSet.CommonEvent;
		if (commonEvent > 0) {
			$gameTemp.reserveCommonEvent(commonEvent);
		}
	}
};

GF.COG.DataManager_extractSaveContents = DataManager.extractSaveContents;
DataManager.extractSaveContents = function (contents) {
    GF.COG.DataManager_extractSaveContents.call(this, contents);
    $gameSystem.checkCOGData();
};

DataManager.loadDatabaseNotetag = function() {
    this.getCOGObjNames();
    this.getCOGSystemNames();
    this.processCOGNotetagsItem($dataItems);
    this.processCOGNotetagsEquip($dataWeapons);
    this.processCOGNotetagsEquip($dataArmors);
    this.processCOGNotetagsEnemy($dataEnemies);
    this.processCOGNotetagsActor($dataActors);
    this.processCOGNotetagsClass($dataClasses);
};

DataManager.getCOGObjNames = function () {
    GF.ActorIdRef = {};
    GF.ClassIdRef = {};
    GF.SkillIdRef = {};
    GF.ItemIdRef = {};
	GF.WeaponIdRef = {};
	GF.ArmorIdRef = {};
	GF.EnemyIdRef = {};
    GF.TroopIdRef = {};
	GF.StateIdRef = {};
	const refList = [
        GF.ActorIdRef, GF.ClassIdRef, GF.SkillIdRef, 
        GF.ItemIdRef, GF.WeaponIdRef, GF.ArmorIdRef, 
        GF.EnemyIdRef, GF.TroopIdRef, GF.StateIdRef
    ];
	const groupList = [
        $dataActors, $dataClasses, $dataSkills, 
        $dataItems, $dataWeapons, $dataArmors, 
        $dataEnemies, $dataTroops, $dataStates
    ];
	for (let i = 0; i < groupList.length; i++) {
		const group = groupList[i];
		const ref = refList[i];
		for (let n = 1; n < group.length; n++) {
			const obj = group[n];
			if (obj.name.length <= 0) continue;
			ref[obj.name.toUpperCase()] = n;
		}
	}
};

DataManager.getCOGSystemNames = function () {
	const group = $dataSystem;
    GF.STypeIdRef = {};
    for (let i = 1; i < group.skillTypes.length; i++) {
        let name = group.skillTypes[i].toUpperCase();
        name = name.replace(/\\I\[(\d+)\]/gi, '');
        GF.STypeIdRef[name] = i;
    }
    GF.ElementIdRef = {};
    for (let i = 1; i < group.elements.length; i++) {
        let name = group.elements[i].toUpperCase();
        name = name.replace(/\\I\[(\d+)\]/gi, '');
        GF.ElementIdRef[name] = i;
    }
    GF.WTypeIdRef = {};
    for (let i = 1; i < group.weaponTypes.length; i++) {
        let name = group.weaponTypes[i].toUpperCase();
        name = name.replace(/\\I\[(\d+)\]/gi, '');
        GF.WTypeIdRef[name] = i;
    }
    GF.ATypeIdRef = {};
    for (let i = 1; i < group.armorTypes.length; i++) {
        let name = group.armorTypes[i].toUpperCase();
        name = name.replace(/\\I\[(\d+)\]/gi, '');
        GF.ATypeIdRef[name] = i;
    }
};

DataManager.processCOGNotetagsItem = function (group) {
    for (let i = 1; i < group.length; i++) {
        const obj = group[i];
        this.setupCOGNotetags1(obj);
    }
};

DataManager.processCOGNotetagsEquip = function (group) {
    for (let i = 1; i < group.length; i++) {
        const obj = group[i];
        this.setupCOGNotetags1(obj);
    }
};

DataManager.processCOGNotetagsEnemy = function (group) {
    for (let i = 1; i < group.length; i++) {
        const obj = group[i];
        this.setupCOGNotetags2(obj);
    }
};

DataManager.processCOGNotetagsActor = function (group) {
    for (let i = 1; i < group.length; i++) {
        const obj = group[i];
        this.setupCOGNotetags3(obj);
    }
};

DataManager.processCOGNotetagsClass = function (group) {
    for (let i = 1; i < group.length; i++) {
        const obj = group[i];
        this.setupCOGNotetags4(obj);
    }
};

DataManager.setupCOGNotetags1 = function (obj) {
	const notedata = obj.note.split(/[\r\n]+/);

	for (let i = 0; i < notedata.length; i++) {
		const line = notedata[i];
		if (line.match(/<(?:Price|价格):\s*(\d+)>/i)) {
			obj.price = Number(RegExp.$1);
		} else if (line.match(/<(.*):\s*([\+\-]\d+)>/i)) {
			if (this.isItem(obj)) continue;
			const stat = String(RegExp.$1);
			const value = Number(RegExp.$2);
			const id = this.getParamId(stat);
			if (id > -1) obj.params[id] = value;
		}
	}
};

DataManager.setupCOGNotetags2 = function (obj) {
	const notedata = obj.note.split(/[\r\n]+/);
	
	for (let i = 0; i < notedata.length; i++) {
		const line = notedata[i];
		if (line.match(/<(?:GOLD):\s*(\d+)>/i)) {
			obj.gold = Number(RegExp.$1);
		} else if (line.match(/<(?:EXP):\s*(\d+)>/i)) {
			obj.exp = Number(RegExp.$1);
		} else if (line.match(/<(.*):\s*(\d+)>/i)) {
			const stat = String(RegExp.$1);
			const value = Number(RegExp.$2);
			const id = this.getParamId(stat);
			if (id > -1) obj.params[id] = value;
		}
	}
};

DataManager.setupCOGNotetags3 = function (obj) {
	const notedata = obj.note.split(/[\r\n]+/);

	for (let i = 0; i < notedata.length; i++) {
		const line = notedata[i];
		if (line.match(/<(?:Max Level|等级上限):\s*(\d+)>/i)) {
			obj.maxLevel = Math.max(1, Number(RegExp.$1));
		} else if (line.match(/<(?:Initial Level|初始等级):\s*(\d+)>/i)) {
			obj.initialLevel = Math.max(1, Number(RegExp.$1));
		}
	}
};

DataManager.setupCOGNotetags4 = function (obj) {
    for (let i = 0; i < obj.learnings.length; i++) {
        const learning = obj.learnings[i];
        if (learning.note.match(/<(?:学习等级|Learn At Level):\s*(\d+)>/i)) {
			learning.level = Math.max(1, Number(RegExp.$1));
		}
    }
};

DataManager.getParamId = function(str) {
	const paramList = ['MHP', 'MMP', 'ATK', 'DEF', 'MAT', 'MDF', 'AGI', 'LUK'];
	return paramList.indexOf(str.toUpperCase());
};

//=============================================================================
// ColorManager
//=============================================================================

ColorManager.loadWindowskin = function() {
    this._windowskin = ImageManager.loadSystem(GF.Param.COGWindowSet.WindowSkin);
};

//=============================================================================
// ImageManager
//=============================================================================

ImageManager.blackBitmap = function() {
    const bitmap = new Bitmap(40, 40);
    bitmap.fillAll('#ffffff');
    return bitmap;
};
/**
 * 
 * @param {string} folderString - 路径格式'xxx/xxx'
 * @returns {Bitmap}
 */
ImageManager.loadCustomBitmap = function(folderString) {
    const list = folderString.split('/');
	if (list.length < 2) return ImageManager.blackBitmap();
	const folder = 'img/' + list[0] + '/';
	const filename = list[1];
	return this.loadBitmap(folder, filename);
};

ImageManager.loadBitmapFromUrl = function(url) {
	const cache = this.cacheSystem(url) ? this._system : this._cache;
    if (!cache[url]) {
        cache[url] = Bitmap.load(url);
    }
    return cache[url];
};

ImageManager.cacheSystem = function(url) {
	return url.includes("/system/");
};

//=============================================================================
// AudioManager
//=============================================================================

AudioManager._masterVolume = 100;
AudioManager._videoVolume = 100;
Object.defineProperty(AudioManager, 'masterVolume', {
    get: function() {
        return this._masterVolume;
    },
    set: function(value) {
        this._masterVolume = value;
        WebAudio.setMasterVolume(this._masterVolume / 100);
    },
    configurable: true
});

Object.defineProperty(AudioManager, 'videoVolume', {
    get: function() {
        return this._videoVolume;
    },
    set: function(value) {
        this._videoVolume = value;
        Video.setVolume(this._videoVolume / 100);
    },
    configurable: true
});

//=============================================================================
// SceneManager
//=============================================================================

if (GF.Param.COGMenuSet.ScenePreCreate) {

SceneManager.changeScene = function() {
    if (this.isSceneChanging() && !this.isCurrentSceneBusy()) {
        if (this._scene) {
            this._scene.terminate();
            this.onSceneTerminate();
        }
        this._scene = this._nextScene;
        this._nextScene = null;
		if (this._scene && !this._canPreCreateScene) {
            this._scene.create();
            this.onSceneCreate();
        }
        if (this._exiting) {
            this.terminate();
        }
    }
};

SceneManager.goto = function(sceneClass) {
    if (sceneClass) {
        this._nextScene = new sceneClass();
    }
    if (this._scene) {
        this._scene.stop();
    }
    if (this.isNextScene(Scene_Shop)) return;
    if (this.isNextScene(Scene_Name)) return;
    this.prepareNextScene();
};

SceneManager.prepareNextScene = function() {
    if (!this._nextScene) return;
    if (this._nextScene.prepare) {
        this._nextScene.prepare(...arguments);
    }
    this._canPreCreateScene = this.canPreCreateScene();
    if (this._canPreCreateScene) {
        this._nextScene.create();
        this.onSceneCreate();
    }
};

SceneManager.canPreCreateScene = function() {
    if (GF.Param.COGMenuSet.BackgroundMode === '自定义图片') {
        if (this.isMenuScene(this._nextScene)) {
            return true;
        }
    }
    if (!this.isMenuScene(this._scene)) return false;
    if (this.isNextScene(Scene_Battle)) return false;
	return true;
};

}

GF.COG.SceneManager_isGameActive = SceneManager.isGameActive;
SceneManager.isGameActive = function() {
	if (GF.Param.COGQoLSet.RequireFocus) {
		return GF.COG.SceneManager_isGameActive.call(this);
	}
    return true;
};

SceneManager.isCurrentScene = function(sceneClass) {
	return this._scene && this._scene.constructor === sceneClass;
};

SceneManager.isSceneAny = function(array) {
	if(!this._scene) return false;
	return array.includes(this._scene.constructor.name);
};

SceneManager.isMenuScene = function(sceneClass) {
	return sceneClass && sceneClass instanceof Scene_MenuBase;
};

SceneManager.stackContains = function(sceneClass) {
	return this._stack.includes(sceneClass);
};

SceneManager.catchNormalError = function (e) {
    Graphics.printFullError(e.name, e.message);
    AudioManager.stopAll();
    console.error(e.stack);
};

SceneManager.catchLoadError = function(e) {
    const url = decodeURI(e[1]);
    const retry = e[2];
	const pre = '系统检测到文件不存在，位置在';
    Graphics.printError(pre, url);
    if (retry) {
        Graphics.showRetryButton(() => {
            retry();
            SceneManager.resume();
        });
    } else {
        AudioManager.stopAll();
    }
	console.error(pre + ': ' + url);
};

SceneManager.catchUnknownError = function(e) {
    Graphics.printError("未知错误", String(e));
    AudioManager.stopAll();
};

GF.COG.SceneManager_initialize = SceneManager.initialize;
SceneManager.initialize = function () {
    GF.COG.SceneManager_initialize.call(this);
    this.finallyChangeFunction();
};

SceneManager.finallyChangeFunction = function () {
};

//=============================================================================
// PluginManager
//=============================================================================

PluginManager.registerCommand(GF.COG.pluginName, 'GoldChange', args => {
    $gameParty.gainGold(Number(args.value));
});

PluginManager.registerCommand(GF.COG.pluginName, 'ForceClose', args => {
	window.close(true);
});

PluginManager.registerCommand(GF.COG.pluginName, 'InputMenuSwitch', args => {
    const type = args.Type;
    const state = eval(args.State || 'true');
    $gameSystem['_inputEnable_' + type] = state;
});

//=============================================================================
// Game_System
//=============================================================================

GF.COG.Game_System_initialize = Game_System.prototype.initialize;
Game_System.prototype.initialize = function() {
	GF.COG.Game_System_initialize.call(this);
	this.initCOGData();
};

Game_System.prototype.initCOGData = function() {
	this._inputEnable_BattleKeyboard = GF.Param.COGInputSet.BattleKeyboard;			
	this._inputEnable_BattlePad = GF.Param.COGInputSet.BattlePad;						
	this._inputEnable_BattleCusor = GF.Param.COGInputSet.BattleCusor;					
	this._inputEnable_BattleTouch = GF.Param.COGInputSet.BattleTouch;			
	
	this._inputEnable_MapKeyboard = GF.Param.COGInputSet.MapKeyboard;
	this._inputEnable_MapPad = GF.Param.COGInputSet.MapPad;
	this._inputEnable_MapCusor = GF.Param.COGInputSet.MapCusor;						
	this._inputEnable_MapTouch = GF.Param.COGInputSet.MapTouch;

    this._inputEnable_MapKeyboardMove = GF.Param.COGInputSet.MapKeyboardMove;
    this._inputEnable_MapWASDMove = GF.Param.COGInputSet.MapWASDMove;
    				
	this._inputEnable_MapLeftMove = GF.Param.COGInputSet.MapLeftMove;		
	this._inputEnable_MapRightMenu = GF.Param.COGInputSet.MapRightMenu;		
	this._inputEnable_MapTouchMenu = GF.Param.COGInputSet.MapTouchMenu;			
	
	this._inputEnable_MenuKeyboard = GF.Param.COGInputSet.MenuKeyboard;				
	this._inputEnable_MenuPad = GF.Param.COGInputSet.MenuPad;							
	this._inputEnable_MenuCusor = GF.Param.COGInputSet.MenuCusor;						
	this._inputEnable_MenuTouch = GF.Param.COGInputSet.MenuTouch;				
};

Game_System.prototype.checkCOGData = function() {
    if (this._inputEnable_BattleKeyboard === undefined) {
        this._inputEnable_BattleKeyboard = GF.Param.COGInputSet.BattleKeyboard;	
    }
    if (this._inputEnable_BattlePad === undefined) {
        this._inputEnable_BattlePad = GF.Param.COGInputSet.BattlePad;
    }
    if (this._inputEnable_BattleCusor === undefined) {
        this._inputEnable_BattleCusor = GF.Param.COGInputSet.BattleCusor;	
    }
    if (this._inputEnable_BattleTouch === undefined) {
        this._inputEnable_BattleTouch = GF.Param.COGInputSet.BattleTouch;
    }

    if (this._inputEnable_MapKeyboard === undefined) {
        this._inputEnable_MapKeyboard = GF.Param.COGInputSet.MapKeyboard;
    }
    if (this._inputEnable_MapPad === undefined) {
        this._inputEnable_MapPad = GF.Param.COGInputSet.MapPad;	
    }
    if (this._inputEnable_MapCusor === undefined) {
        this._inputEnable_MapCusor = GF.Param.COGInputSet.MapCusor;
    }
    if (this._inputEnable_MapTouch === undefined) {
        this._inputEnable_MapTouch = GF.Param.COGInputSet.MapTouch;
    }

    if (this._inputEnable_MapKeyboardMove === undefined) {
        this._inputEnable_MapKeyboardMove = GF.Param.COGInputSet.MapKeyboardMove;
    }
    if (this._inputEnable_MapWASDMove === undefined) {
        this._inputEnable_MapWASDMove = GF.Param.COGInputSet.MapWASDMove;
    }
    if (this._inputEnable_MapLeftMove === undefined) {
        this._inputEnable_MapLeftMove = GF.Param.COGInputSet.MapLeftMove;
    }
    if (this._inputEnable_MapRightMenu === undefined) {
        this._inputEnable_MapRightMenu = GF.Param.COGInputSet.MapRightMenu;
    }
    if (this._inputEnable_MapTouchMenu === undefined) {
        this._inputEnable_MapTouchMenu = GF.Param.COGInputSet.MapTouchMenu;
    }

    if (this._inputEnable_MenuKeyboard === undefined) {
        this._inputEnable_MenuKeyboard = GF.Param.COGInputSet.MenuKeyboard;	
    }
    if (this._inputEnable_MenuPad === undefined) {
        this._inputEnable_MenuPad = GF.Param.COGInputSet.MenuPad;
    }
    if (this._inputEnable_MenuCusor === undefined) {
        this._inputEnable_MenuCusor = GF.Param.COGInputSet.MenuCusor;
    }
	if (this._inputEnable_MenuTouch === undefined) {
	    this._inputEnable_MenuTouch = GF.Param.COGInputSet.MenuTouch;
    }
};

//=============================================================================
// Game_Actor
//=============================================================================

Game_Actor.prototype.changeClass = function (classId, keepExp) {
    this._bypassLevelUpEffect = true;
    if (keepExp) {
        this._exp[classId] = this.currentExp();
    }
    this._classId = classId;
    this.changeExp(this._exp[this._classId] || 0, false);
    this.refresh();
    this._bypassLevelUpEffect = false;
};

GF.COG.Game_Actor_paramBase = Game_Actor.prototype.paramBase;
Game_Actor.prototype.paramBase = function (paramId) {
    if (this.level > 99) {
        let i = this.currentClass().params[paramId][99];
        let j = this.currentClass().params[paramId][98];
        i += (i - j) * (this.level - 99);
        return i;
    }
    return GF.COG.Game_Actor_paramBase.call(this, paramId);
};

//=============================================================================
// Game_Party
//=============================================================================

Game_Party.prototype.maxGold = function() {
    return GF.Param.COGGoldSet.GoldMax;
};

Game_Party.prototype.setupBattleTestItems = function() {
	const num = GF.Param.COGTestSet.BTestAddedQuantity;
	const testItem = GF.Param.COGTestSet.BTestItems;
	const testWeapon = GF.Param.COGTestSet.BTestWeapons;
	const testArmor = GF.Param.COGTestSet.BTestArmors;
	if (testItem) this.testAddItem($dataItems, num);
	if (testWeapon) this.testAddItem($dataWeapons, num);
	if (testArmor) this.testAddItem($dataArmors, num);
};

Game_Party.prototype.testAddItem = function(group, num) {
	for (const item of group) {
        if (item && item.name.length > 0) {
			let maxNum = this.maxItems(item);
            this.gainItem(item, num < maxNum ? num : maxNum);
        }
    }
};

//=============================================================================
// Game_Character
//=============================================================================

GF.COG.Game_Character_processMoveCommand = Game_Character.prototype.processMoveCommand;
Game_Character.prototype.processMoveCommand = function (command) {
    const gc = Game_Character;
    const params = command.parameters;
    switch (command.code) {
        case gc.ROUTE_SCRIPT:
            try {
                const func = new Function(params[0]);
                func.call(this);
            } catch (e) {
                GF.Util.displayError(e, params[0], '运动脚本代码错误');
            }
            return;
        }
    return GF.COG.Game_Character_processMoveCommand.call(this, command);
};

//=============================================================================
// Game_Interpreter
//=============================================================================

GF.COG.Game_Interpreter_command111 = Game_Interpreter.prototype.command111;
Game_Interpreter.prototype.command111 = function(params) {
    switch (params[0]) {
        case 12: // Script
            let result = false;
            try {
                const func = new Function(params[1]);
                result = !!func.call(this);
            } catch (e) {
                GF.Util.displayError(e, params[1], '分支脚本代码错误');
            }
            this._branch[this._indent] = result;
            if (this._branch[this._indent] === false) {
                this.skipBranch();
            }
            return true;
    }
    return GF.COG.Game_Interpreter_command111.call(this, params);
};

Game_Interpreter.prototype.command122 = function(params) {
    const startId = params[0];
    const endId = params[1];
    const operationType = params[2];
    const operand = params[3];
    let value = 0;
    let randomMax = 1;
    switch (operand) {
        case 0: // Constant
            value = params[4];
            break;
        case 1: // Variable
            value = $gameVariables.value(params[4]);
            break;
        case 2: // Random
            value = params[4];
            randomMax = params[5] - params[4] + 1;
            randomMax = Math.max(randomMax, 1);
            break;
        case 3: // Game Data
            value = this.gameDataOperand(params[4], params[5], params[6]);
            break;
        case 4: // Script
            try {
                const func = new Function('return ' + params[4]);
                value = func.call(this);
            } catch (e) {
                GF.Util.displayError(e, params[4], '变量脚本代码错误');
            }
            break;
    }
    for (let i = startId; i <= endId; i++) {
        if (typeof value === "number") {
            const realValue = value + Math.randomInt(randomMax);
            this.operateVariable(i, operationType, realValue);
        } else {
            this.operateVariable(i, operationType, value);
        }
    }
    return true;
};

Game_Interpreter.prototype.command355 = function() {
    let script = this.currentCommand().parameters[0] + "\n";
    while (this.nextEventCode() === 655) {
        this._index++;
        script += this.currentCommand().parameters[0] + "\n";
    }
    try {
        const func = new Function(script);
        func.call(this);
    } catch (e) {
        GF.Util.displayError(e, script, '事件脚本代码错误');
    }
    return true;
};

//=============================================================================
// Game_Player
//=============================================================================

GF.COG.SceneManager_finallyChangeFunction = SceneManager.finallyChangeFunction;
SceneManager.finallyChangeFunction = function () {
    GF.COG.Game_Player_getInputDirection = Game_Player.prototype.getInputDirection;
    Game_Player.prototype.getInputDirection = function () {
        if (SceneManager.isCurrentScene(Scene_Map)) {
            if ($gameSystem && !$gameSystem._inputEnable_MapKeyboardMove) {
                return 0;
            }
        }
        return GF.COG.Game_Player_getInputDirection.call(this);
    };
    GF.COG.SceneManager_finallyChangeFunction.call(this);
};

//=============================================================================
// Game_Event
//=============================================================================

if (GF.Param.COGQoLSet.SmartEventCollision) {
	
Game_Event.prototype.isCollidedWithEvents = function (x, y) {
    const events = $gameMap.eventsXyNt(x, y).filter(ev => ev.isNormalPriority());
    return events.length > 0 && this.isNormalPriority();
};

}

//=============================================================================
// Game_Map
//=============================================================================

Game_Map.prototype.isEventExist = function (eventId) {
    if (!eventId) return false;
    const event = this.event(eventId);
    if (!event) {
        alert("插件指令错误，当前地图并不存在id为" + eventId + "的事件。");
        return false;
    }
    return true;
};

//=============================================================================
// Game_Action
//=============================================================================

Game_Action.prototype.evalDamageFormula = function(target) {
	const item = this.item();
	const a = this.subject(); // eslint-disable-line no-unused-vars
	const b = target; // eslint-disable-line no-unused-vars
	const v = $gameVariables._data; // eslint-disable-line no-unused-vars
	const sign = [3, 4].includes(item.damage.type) ? -1 : 1;
    try {
        const func = new Function('a', 'b', 'v', 'return ' + item.damage.formula);
        const value = Math.max(func.call(this), 0) * sign;
        return isNaN(value) ? 0 : value;
    } catch (e) {
		GF.Util.displayError(e, item.damage.formula, '参数公式错误');
        return 0;
    }
};

//=============================================================================
// Sprite_Animation
//=============================================================================

Sprite_Animation.prototype.setViewport = function(renderer) {
	const mirrorOffset = GF.Param.COGQoLSet.AniMirrorOffset;
	const mirror = this._mirror && mirrorOffset ? -1 : 1;
    const vw = this._viewportSize;
    const vh = this._viewportSize;
    const vx = (this._animation.offsetX - vw / 2) * mirror;
    const vy = this._animation.offsetY - vh / 2;
    const pos = this.targetPosition(renderer);
    renderer.gl.viewport(vx + pos.x, vy + pos.y, vw, vh);
};

//=============================================================================
// WindowLayer
//=============================================================================

GF.COG.WindowLayer_render = WindowLayer.prototype.render;
WindowLayer.prototype.render = function(renderer) {
    if (GF.Param.COGWindowSet.MutlipleLayers) {
        PIXI.Container.prototype.render.call(this, renderer);
    } else {
        GF.COG.WindowLayer_render.call(this, renderer);
    }
};

//=============================================================================
// Window
//=============================================================================

GF.COG.Window_createAllParts = Window.prototype._createAllParts;
Window.prototype._createAllParts = function () {
    this._layoutSprite = new Sprite();
    this.addChild(this._layoutSprite);
    GF.COG.Window_createAllParts.call(this);
};

Object.defineProperty(Window.prototype, 'layoutOpacity', {
    get: function () {
        return this._layoutSprite.alpha * 255;
    },
    set: function (value) {
        this._layoutSprite.alpha = value.clamp(0, 255) / 255;
    },
    configurable: true
});

Object.defineProperty(Window.prototype, 'contentsBackOpacity', {
    get: function () {
        return this._contentsBackSprite.alpha * 255;
    },
    set: function (value) {
        this._contentsBackSprite.alpha = value.clamp(0, 255) / 255;
    },
    configurable: true
});

Object.defineProperty(Window.prototype, "openness", {
    get: function() {
        return this._openness;
    },
    set: function(value) {
        if (this._openness !== value) {
            this._openness = value.clamp(0, 255);
            this._container.scale.y = this._openness / 255;
            this._container.y = (this.height / 2) * (1 - this._openness / 255);
			this._layoutSprite.scale.y = this._container.scale.y;
            this._layoutSprite.y = this._container.y;
        }
    },
    configurable: true
});

//=============================================================================
// Window_Base
//=============================================================================

GF.COG.Window_Base_initialize = Window_Base.prototype.initialize;
Window_Base.prototype.initialize = function(rect) {
    if (!rect) rect = new Rectangle(0, 0, 100, 100);
    GF.COG.Window_Base_initialize.call(this, rect);
};

Window_Base.prototype.loadWindowskin = function() {
    this.windowskin = ImageManager.loadSystem(GF.Param.COGWindowSet.WindowSkin);
};

Window_Base.prototype.lineHeight = function() {
    return GF.Param.COGWindowSet.LineHeight;
};

Window_Base.prototype.itemPadding = function() {
    return GF.Param.COGWindowSet.ItemPadding;
};

Window_Base.prototype.translucentOpacity = function() {
	return GF.Param.COGWindowSet.TranslucentOpacity;
};

Window_Base.prototype.standardFontFace = function() {
    return $gameSystem.mainFontFace();
};

Window_Base.prototype.standardFontSize = function() {
    return $gameSystem.mainFontSize();
};

Window_Base.prototype.resetFontSettings = function() {
    this.contents.fontFace = this.standardFontFace();
    this.contents.fontSize = this.standardFontSize();
	this.resetTextColor();
};

Window_Base.prototype.calcTextHeight = function(textState) {
    const lineSpacing = this.lineHeight() - this.standardFontSize();
    const lastFontSize = this.contents.fontSize;
    const lines = textState.text.slice(textState.index).split("\n");
    const textHeight = this.maxFontSizeInLine(lines[0]) + lineSpacing;
    this.contents.fontSize = lastFontSize;
    return textHeight;
};

Window_Base.prototype.textWidthEx = function (text) {
	return this.textSizeEx(text).width;
};

Window_Base.prototype.textHeightEx = function (text) {
	return this.textSizeEx(text).height;
};

GF.COG.Window_Base_changePaintOpacity = Window_Base.prototype.changePaintOpacity;
Window_Base.prototype.changePaintOpacity = function(enabled) {
	GF.COG.Window_Base_changePaintOpacity.call(this, enabled);
    this.contentsBack.paintOpacity = enabled ? 255 : this.translucentOpacity();
};

Window_Base.prototype.setOpenCallback = function(callback) {
	this._openCallback = callback;
};

Window_Base.prototype.setCloseCallback = function(callback) {
	this._closeCallback = callback;
};

Window_Base.prototype.updateOpen = function() {
	if (this._opening) {
		this.openness += GF.Param.COGWindowSet.OpenSpeed;
		if (this.isOpen()) {
			this._opening = false;
			if(this._openCallback) {
				this._openCallback();
			}
		}
	}
};

Window_Base.prototype.updateClose = function() {
	if (this._closing) {
		this.openness -= GF.Param.COGWindowSet.CloseSpeed;
		if (this.isClosed()) {
			this._closing = false;
			if(this._closeCallback) {
				this._closeCallback();
			}
		}
	}
};

Window_Base.prototype.drawCurrencyValue = function (value, unit, x, y, width) {
    const unitWidth = Math.min(80, this.textWidth(unit));
    this.resetTextColor();
    const cx = this.usingGoldIcon(unit) ? ImageManager.iconWidth : unitWidth;
    let text = String(value);
    if (this.textWidth(text) > width - cx) {
        text = GF.Param.COGGoldSet.GoldOverlap;
    }
    this.drawText(text, x, y, width - cx - 4, 'right');
    if (this.usingGoldIcon(unit)) {
        this.drawIcon(GF.Param.COGGoldSet.GoldIcon, x + width - ImageManager.iconWidth, y + 2);
    } else {
		this.changeTextColor(ColorManager.systemColor());
		this.drawText(unit, x, y, width, 'right');
	}
    this.resetFontSettings();
};

Window_Base.prototype.usingGoldIcon = function(unit) {
    if (unit !== TextManager.currencyUnit) return false;
    return GF.Param.COGGoldSet.GoldIcon > 0;
};

Window_Base.prototype.refreshDimmerBitmap = function() {
    if (this._dimmerSprite) {
        const bitmap = this._dimmerSprite.bitmap;
        const w = this.width > 0 ? this.width + 8 : 0;
        const h = this.height;
        const m = this.padding;
        const c1 = ColorManager.dimColor1();
        const c2 = ColorManager.dimColor2();
        bitmap.resize(w, h);
        bitmap.gradientFillNormalRect(0, 0, w, m, c2, c1, true);
        bitmap.fillNormalRect(0, m, w, h - m * 2, c1);
        bitmap.gradientFillNormalRect(0, h - m, w, m, c1, c2, true);
        this._dimmerSprite.setFrame(0, 0, w, h);
    }
};

//=============================================================================
// Window_Selectable
//=============================================================================

GF.COG.Window_Selectable_initialize = Window_Selectable.prototype.initialize;
Window_Selectable.prototype.initialize = function (rect) {
    GF.COG.Window_Selectable_initialize.call(this, rect);
    this._isPointing = false;
};

Window_Selectable.prototype.colSpacing = function() {
    return GF.Param.COGWindowSet.ColSpacing;
};

Window_Selectable.prototype.rowSpacing = function() {
    return GF.Param.COGWindowSet.RowSpacing;
};

Window_Selectable.prototype.itemHeight = function() {
    return Window_Scrollable.prototype.itemHeight.call(this) + GF.Param.COGWindowSet.ItemHeight;
};

Window_Selectable.prototype.isBeingTouched = function () {
    return this.active && this.isTouchedInsideFrame() && this._isPointing;
};

Window_Selectable.prototype.onTouchSelect = function(trigger) {
    this._doubleTouch = false;
    if (this.isCursorMovable()) {
        const lastIndex = this.index();
        const hitIndex = this.hitIndex();
        if (hitIndex >= 0) {
            if (hitIndex === this.index()) {
                this._doubleTouch = true;
            }
			this._isPointing = true;
            this.select(hitIndex);
        } else {
			this._isPointing = false;
		}
        if (trigger && this.index() !== lastIndex) {
            this.playCursorSound();
        }
    }
};

Window_Selectable.prototype.cursorDown = function(wrap) {
    const index = this.index();
    const maxItems = this.maxItems();
    const maxCols = this.maxCols();
    if (index < maxItems - maxCols || (wrap && maxCols === 1)) {
        this.smoothSelect((index + maxCols) % maxItems);
    } else if (index >= maxItems - maxCols && index < maxItems) {
		if (maxItems % maxCols) {
			this.smoothSelect((index + maxItems % maxCols) % maxItems);
		} else {
			this.smoothSelect((index + maxCols) % maxItems);
		}
    }
};

Window_Selectable.prototype.cursorUp = function(wrap) {
    const index = Math.max(0, this.index());
    const maxItems = this.maxItems();
    const maxCols = this.maxCols();
    if (index >= maxCols || (wrap && maxCols === 1)) {
        this.smoothSelect((index - maxCols + maxItems) % maxItems);
    } else if (index >= 0 && index < maxCols) {
		if (maxItems % maxCols) {
			this.smoothSelect((index - maxItems % maxCols + maxItems) % maxItems);
		} else {
			this.smoothSelect((index - maxCols + maxItems) % maxItems);
		}
	}
};

Window_Selectable.prototype.cursorRight = function(wrap) {
    const index = this.index();
    const maxItems = this.maxItems();
    const maxCols = this.maxCols();
    const horizontal = this.isHorizontal();
    if (maxCols >= 1 && (index < maxItems || (wrap && horizontal))) {
        this.smoothSelect((index + 1) % maxItems);
    }
};

Window_Selectable.prototype.cursorLeft = function(wrap) {
    const index = Math.max(0, this.index());
    const maxItems = this.maxItems();
    const maxCols = this.maxCols();
    const horizontal = this.isHorizontal();
    if (maxCols >= 1 && (index >= 0 || (wrap && horizontal))) {
        this.smoothSelect((index - 1 + maxItems) % maxItems);
    }
};

//=============================================================================
// Window_Gold
//=============================================================================

Window_Gold.prototype.drawCurrencyValue = function (value, unit, x, y, width) {
	if (GF.Param.COGGoldSet.GoldWindowType === '数字+图标') {
		Window_Base.prototype.drawCurrencyValue.call(this, value, unit, x, y, width);
		return;
	}
    const unitWidth = Math.min(80, this.textWidth(unit));
    this.resetTextColor();
    const cx = this.usingGoldIcon(unit) ? ImageManager.iconWidth + unitWidth : unitWidth;
    let text = String(value);
    if (this.textWidth(text) > width - cx) {
        text = GF.Param.COGGoldSet.GoldOverlap;
    }
    this.drawText(text, x, y, width - 4, 'right');
    if (this.usingGoldIcon(unit)) {
        this.drawIcon(GF.Param.COGGoldSet.GoldIcon, x, y + 2);
    } 
	this.changeTextColor(ColorManager.systemColor());
	this.drawText(unit, x + ImageManager.iconWidth + 4, y, width, 'left');
    this.resetFontSettings();
};

//=============================================================================
// Window_MapName
//=============================================================================

Window_MapName.prototype.drawBackground = function(x, y, width, height) {
    const color1 = ColorManager.dimColor1();
    const color2 = ColorManager.dimColor2();
    const half = width / 2;
    this.contents.gradientFillNormalRect(x, y, half, height, color2, color1);
    this.contents.gradientFillNormalRect(x + half, y, half, height, color1, color2);
};

//=============================================================================
// Window_DebugEdit
//=============================================================================

Window_DebugEdit.prototype.cursorRight = function(wrap) {
};

Window_DebugEdit.prototype.cursorLeft = function(wrap) {
};

//=============================================================================
// Scene_Base
//=============================================================================

GF.COG.Scene_Base_initialize = Scene_Base.prototype.initialize;
Scene_Base.prototype.initialize = function() {
	GF.COG.Scene_Base_initialize.call(this);
	this._fadeCallback = null;
    this._scenePopDelay = 0;
};

GF.COG.Scene_Base_updateFade = Scene_Base.prototype.updateFade;
Scene_Base.prototype.updateFade = function() {
	const prevDuration = this._fadeDuration;
	GF.COG.Scene_Base_updateFade.call(this);
	if (prevDuration === 1) {
		this.callFadeCallback();
	}
};

Scene_Base.prototype.setFadeCallback = function(callback) {
	this._fadeCallback = callback;
};

Scene_Base.prototype.callFadeCallback = function() {
	if (this._fadeCallback) {
		this._fadeCallback();
		this._fadeCallback = null;
	}
};

Scene_Base.prototype.removeWindow = function(window) {
    this._windowLayer.removeChild(window);
};

GF.COG.Scene_Base_update = Scene_Base.prototype.update;
Scene_Base.prototype.update = function () {
    GF.COG.Scene_Base_update.call(this);
    this.updateMouseCursorStyle();
    this._scenePopDelay--;
};

Scene_Base.prototype.updateMouseCursorStyle = function () {
	if (!GF.Param.COGQoLSet.UpdateCusorStyle) return;
	if (!TouchInput.isHovered()) return;
    if (!this._windowLayer) return TouchInput.setCursor('default');
	for (const win of this._windowLayer.children) {
		if (typeof win.isBeingTouched === 'function' && win.isBeingTouched()) {
			return TouchInput.setCursor('pointer');
		}
	}
    TouchInput.setCursor('default');
};

GF.COG.Scene_Base_isBusy = Scene_Base.prototype.isBusy;
Scene_Base.prototype.isBusy = function() {
	return (
		this._scenePopDelay > 0 ||
		GF.COG.Scene_Base_isBusy.call(this)
	);
};

Scene_Base.prototype.setPopDelay = function(delay) {
	this._scenePopDelay = delay;
};

GF.COG.Scene_Base_stop = Scene_Base.prototype.stop;
Scene_Base.prototype.stop = function() {
	GF.COG.Scene_Base_stop.call(this);
    this.startAllPartInvert();
};

Scene_Base.prototype.startAllPartInvert = function() {};

GF.COG.Scene_Base_createWindowLayer = Scene_Base.prototype.createWindowLayer;
Scene_Base.prototype.createWindowLayer = function () {
    GF.COG.Scene_Base_createWindowLayer.call(this);
    this._foregroundSprite = new Sprite();
    this.addChild(this._foregroundSprite);
};

//=============================================================================
// Scene_Title
//=============================================================================

Scene_Title.prototype.terminate = function() {
    Scene_Base.prototype.terminate.call(this);
    if (GF.Param.COGMenuSet.BackgroundMode !== '自定义图片') {
        SceneManager.snapForBackground();
    }
    if (this._gameTitleSprite) {
        this._gameTitleSprite.bitmap.destroy();
    }
};

GF.COG.Scene_Title_createBackground = Scene_Title.prototype.createBackground;
Scene_Title.prototype.createBackground = function() {
    GF.COG.Scene_Title_createBackground.call(this);
    this._backField = new Container_Base();
	this.addChild(this._backField);
};

//=============================================================================
// Scene_MenuBase
//=============================================================================

Scene_MenuBase.prototype.createBackground = function() {
    this._backgroundSprite = new Sprite();
    const mode = GF.Param.COGMenuSet.BackgroundMode;
    if (mode === '地图截图') {
        this._backgroundSprite.bitmap = SceneManager.backgroundBitmap();
    } else if (mode === '地图截图+模糊滤镜') {
        this._backgroundFilter = new PIXI.filters.BlurFilter();
        this._backgroundSprite.bitmap = SceneManager.backgroundBitmap();
        this._backgroundSprite.filters = [this._backgroundFilter];
    } else if (mode === '地图截图后模糊处理') {
        const bitmap = Bitmap.copy(SceneManager.backgroundBitmap());
        this._backgroundSprite.bitmap = bitmap.blur();
    } else {
        const img = GF.Param.COGMenuSet.CustomBackFile;
        this._backgroundSprite.bitmap = ImageManager.loadCustomBitmap(img);
    } 
    this.addChild(this._backgroundSprite);
    const opacity = GF.Param.COGMenuSet.BackOpacity;
    this.setBackgroundOpacity(opacity);
	this._backField = new Container_Base();
	this.addChild(this._backField);	
};

Scene_MenuBase.prototype.createMainLayout = function(img) {
	if (img === '') return;
	const layout = new Sprite(ImageManager.loadCustomBitmap(img));
	this._backField.addChild(layout);	
};


Scene_MenuBase.prototype.startAllPartInvert = function() {
    Scene_Base.prototype.startAllPartInvert.call(this);
};

//=============================================================================
// Scene_Map
//=============================================================================

GF.COG.Scene_Map_processMapTouch = Scene_Map.prototype.processMapTouch;
Scene_Map.prototype.processMapTouch = function () {
    if ($gameSystem && !$gameSystem._inputEnable_MapLeftMove) return;
    GF.COG.Scene_Map_processMapTouch.call(this);
};

//=============================================================================
// Scene_Boot
//=============================================================================

GF.COG.Scene_Boot_onDatabaseLoaded = Scene_Boot.prototype.onDatabaseLoaded;
Scene_Boot.prototype.onDatabaseLoaded = function() {
    DataManager.loadDatabaseNotetag();
    GF.COG.Scene_Boot_onDatabaseLoaded.call(this);
};

GF.COG.Scene_Boot_start = Scene_Boot.prototype.start;
Scene_Boot.prototype.start = function() {
    GF.COG.Scene_Boot_start.call(this);
    GF.Util.openConsole();
};

GF.COG.Scene_Boot_startNormalGame = Scene_Boot.prototype.startNormalGame;
Scene_Boot.prototype.startNormalGame = function () {
	if (!this.isAutoStartNewGame()) {
		return GF.COG.Scene_Boot_startNormalGame.call(this);
	}
	this.checkPlayerLocation();
	DataManager.setupNewGame();
	SceneManager.goto(Scene_Map);
};

Scene_Boot.prototype.isAutoStartNewGame = function () {
    return GF.Param.COGTestSet.AutoStart && Utils.isNwjs() && Utils.isOptionValid('test');
};

//=============================================================================
// Container_Base
//=============================================================================

class Container_Base extends PIXI.Container {
    constructor() {
        super();
        this.initialize(...arguments);
    }

    initialize() {

    }

    destroy() {
        const options = { children: true, texture: true };
        super.destroy(options);
    }
    
    get active() {
        return this._active;
    }

    set active(value) {
        this._active = !!value;
    }

    activate() {
        this._active = true;
    }

    deactivate() {
        this._active = false;
    }

    show() {
        this.visible = true;
    }

    hide() {
        this.visible = false;
    }

    move(x, y) {
        this.x = x;
        this.y = y;
    }

    update() {
        for (const child of this.children) {
            if (child.update) {
                child.update();
            }
        }
    }
}

//=============================================================================
// Utilities
//=============================================================================

GF.Util = GF.Util || {};

GF.Util.openConsole = function () {
	if (GF._openedConsole) return;
    GF._openedConsole = true;
    if (!GF.Param.COGTestSet.OpenConsole) return;
	if (!nw) return;
	SceneManager.showDevTools();
	setTimeout(() => nw.Window.get().focus(), 500);
};

GF.Util.displayError = function (e, code, message) {
    console.log(message);
    console.log(code);
    console.error(e);
};

GF.Util.getRange = function(n, m) {
    let result = [];
    for (let i = n; i <= m; i++) {
        result.push(i);
    }
    return result;
};

GF.Util.randomInt = function (min, max) {
    if (max < min) [min, max] = [max, min];
    return Math.floor(Math.random() * (max + 1 - min)) + min;
};

GF.Util.looseEqual = function(a, b) {
    if (a === b) return true;
    if (typeof(a) !== typeof(b)) return false;
    const isObjectA = this.isObject(a);
    const isObjectB = this.isObject(b);
    if (isObjectA && isObjectB) {
        try {
            const isArrayA = Array.isArray(a);
            const isArrayB = Array.isArray(b);
            if (isArrayA && isArrayB) {
                if (a.length === b.length) {
                    return a.every((e, i) => this.looseEqual(e, b[i]));
                }
            } else if (!isArrayA && !isArrayB) {
                const keysA = Object.keys(a);
                const keysB = Object.keys(b);
                if (keysA.length === keysB.length){
                    return keysA.every((key) => this.looseEqual(a[key], b[key]));
                }
            }
        } catch (e) {
            return false;
        }
    } else if (!isObjectA && !isObjectB) {
        return String(a) === String(b);
    } 
    return false;
};

GF.Util.isObject = function(obj) {
    return obj !== null && typeof(obj) === 'object';
};

GF.Util.getRandomIdInList = function (array) {
	const diceRoll = Math.floor(Math.random() * array.length);
	const selected = array[diceRoll];
	return selected;
};
/**
 * 按权重随机选择列表中的一个值
 * @param {Array} array {value: value, weight: weight}
 * @returns 
 */
GF.Util.getRandomWeightIdInList = function (array) {
    const totalWeight = array.reduce((a, ele) => a + ele.weight, 0);
    const randWeight = Math.random() * totalWeight;
    let accWeight = 0;
    for (let i = 0; i < array.length; i++) {
        const ele = array[i];
        accWeight += ele.weight;
        if (randWeight < accWeight) {
            return ele.value;
        }
    }
    return array[array.length - 1].value;
};

//=============================================================================
// End of File
//=============================================================================